<!DOCTYPE html>
<html lang="en">
    <head hexo-theme='https://github.com/volantis-x/hexo-theme-volantis/#5.8.0'>
  <meta name="generator" content="Hexo 6.3.0">
  <meta name="Volantis" content="5.8.0">
  <meta charset="utf-8">
  <!-- SEO相关 -->
  
  <link rel="canonical" href="http://8.219.11.28/2024/02/23/工具-redis/"/>
  <!-- 渲染优化 -->
    <meta http-equiv='x-dns-prefetch-control' content='on' />
      <link rel='dns-prefetch' href='https://unpkg.com'>
      <link rel="preconnect" href="https://unpkg.com" crossorigin>
  <meta name="renderer" content="webkit">
  <meta name="force-rendering" content="webkit">
  <meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">
    <meta http-equiv="Content-Security-Policy" content=" default-src 'self' https:; block-all-mixed-content; base-uri 'self' https:; form-action 'self' https:; worker-src 'self' https:; connect-src 'self' https: *; img-src 'self' data: https: *; media-src 'self' https: *; font-src 'self' data: https: *; frame-src 'self' https: *; manifest-src 'self' https: *; child-src https:; script-src 'self' https: 'unsafe-inline' *; style-src 'self' https: 'unsafe-inline' *; ">
  <meta name="HandheldFriendly" content="True" >
  <meta name="apple-mobile-web-app-capable" content="yes">
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=5">
  <meta content="black-translucent" name="apple-mobile-web-app-status-bar-style">
  <meta content="telephone=no" name="format-detection">
  <!-- import head_begin begin -->
  <!-- import head_begin end -->
  <!-- Custom Files headBegin begin-->
  
  <!-- Custom Files headBegin end-->
  <!-- front-matter head_begin begin -->
  <!-- front-matter head_begin end -->
  <link rel="preload" href="/css/style.css" as="style">
  <link rel="preload" href="https://unpkg.com/volantis-static@0.0.1654736714924/media/fonts/VarelaRound/VarelaRound-Regular.ttf" as="font" type="font/ttf" crossorigin="anonymous">
<link rel="preload" href="https://unpkg.com/volantis-static@0.0.1654736714924/media/fonts/UbuntuMono/UbuntuMono-Regular.ttf" as="font" type="font/ttf" crossorigin="anonymous">

  <!-- feed -->
  <!-- 页面元数据 -->
  <title>欢迎来到我的个人博客~</title>
  <meta name="keywords" content="null">
  <meta desc name="description" content="这些技术大部分是自己总结的，非官方 - 天明 - 欢迎来到我的个人博客~">
  
<meta property="og:type" content="article">
<meta property="og:title" content="欢迎来到我的个人博客~">
<meta property="og:url" content="http://8.219.11.28/2024/02/23/%E5%B7%A5%E5%85%B7-Redis/index.html">
<meta property="og:site_name" content="欢迎来到我的个人博客~">
<meta property="og:description" content="基础篇Redis1.Redis简单介绍Redis是一种键值型的NoSql数据库，这里有两个关键字：  键值型 NoSql  其中键值型，是指Redis中存储的数据都是以key.value对的形式存储，而value的形式多种多样，可以是字符串.数值.甚至json：  而NoSql则是相对于传统关系型数据库而言，有很大差异的一种数据库。 对于存储的数据，没有类似Mysql那么严格的约束，比如唯一性，是">
<meta property="og:locale" content="en_US">
<meta property="og:image" content="https://unpkg.com/volantis-static@0.0.1654736714924/media/org.volantis/blog/favicon/android-chrome-192x192.png">
<meta property="article:published_time" content="2024-02-23T14:27:45.468Z">
<meta property="article:modified_time" content="2024-01-25T07:35:02.135Z">
<meta property="article:author" content="天明">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://unpkg.com/volantis-static@0.0.1654736714924/media/org.volantis/blog/favicon/android-chrome-192x192.png">
  <style>
    /* 首屏样式 */
    #safearea {
  display: none;
}
:root {
  --color-site-body: #f4f4f4;
  --color-site-bg: #f4f4f4;
  --color-site-inner: #fff;
  --color-site-footer: #666;
  --color-card: #fff;
  --color-text: #444;
  --color-block: #f6f6f6;
  --color-inlinecode: #c74f00;
  --color-codeblock: #fff7ea;
  --color-h1: #3a3a3a;
  --color-h2: #3a3a3a;
  --color-h3: #333;
  --color-h4: #444;
  --color-h5: #555;
  --color-h6: #666;
  --color-p: #444;
  --color-list: #666;
  --color-list-hl: #30ad91;
  --color-meta: #888;
  --color-read-bkg: #e0d8c8;
  --color-read-post: #f8f1e2;
  --color-copyright-bkg: #f5f5f5;
}
* {
  box-sizing: border-box;
  -webkit-box-sizing: border-box;
  -moz-box-sizing: border-box;
  outline: none;
  margin: 0;
  padding: 0;
}
*::-webkit-scrollbar {
  height: 4px;
  width: 4px;
}
*::-webkit-scrollbar-track-piece {
  background: transparent;
}
*::-webkit-scrollbar-thumb {
  background: #3dd9b6;
  cursor: pointer;
  border-radius: 2px;
  -webkit-border-radius: 2px;
}
*::-webkit-scrollbar-thumb:hover {
  background: #ff5722;
}
html {
  color: var(--color-text);
  width: 100%;
  height: 100%;
  font-family: UbuntuMono, "Varela Round", "PingFang SC", "Microsoft YaHei", Helvetica, Arial, Menlo, Monaco, monospace, sans-serif;
  font-size: 16px;
}
html >::-webkit-scrollbar {
  height: 4px;
  width: 4px;
}
html >::-webkit-scrollbar-track-piece {
  background: transparent;
}
html >::-webkit-scrollbar-thumb {
  background: #54b5a0 linear-gradient(45deg, rgba(255,255,255,0.4) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.4) 50%, rgba(255,255,255,0.4) 75%, transparent 75%, transparent);
  cursor: pointer;
  border-radius: 2px;
  -webkit-border-radius: 2px;
}
html >::-webkit-scrollbar-thumb:hover {
  background: #54b5a0 linear-gradient(45deg, rgba(255,255,255,0.4) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.4) 50%, rgba(255,255,255,0.4) 75%, transparent 75%, transparent);
}
body {
  background-color: var(--color-site-body);
  text-rendering: optimizelegibility;
  -webkit-tap-highlight-color: rgba(0,0,0,0);
  line-height: 1.6;
  -webkit-text-size-adjust: 100%;
  -ms-text-size-adjust: 100%;
}
body.modal-active {
  overflow: hidden;
}
@media screen and (max-width: 680px) {
  body.modal-active {
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
  }
}
a {
  color: #2092ec;
  cursor: pointer;
  text-decoration: none;
  transition: all 0.28s ease;
  -webkit-transition: all 0.28s ease;
  -khtml-transition: all 0.28s ease;
  -moz-transition: all 0.28s ease;
  -o-transition: all 0.28s ease;
  -ms-transition: all 0.28s ease;
}
a:hover {
  color: #ff5722;
}
a:active,
a:hover {
  outline: 0;
}
ul,
ol {
  padding-left: 0;
}
ul li,
ol li {
  list-style: none;
}
header {
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: block;
}
img {
  border: 0;
  background: none;
  max-width: 100%;
}
svg:not(:root) {
  overflow: hidden;
}
hr {
  -moz-box-sizing: content-box;
  box-sizing: content-box;
  -webkit-box-sizing: content-box;
  -moz-box-sizing: content-box;
  height: 0;
  border: 0;
  border-radius: 1px;
  -webkit-border-radius: 1px;
  border-bottom: 1px solid rgba(68,68,68,0.1);
}
button,
input {
  color: inherit;
  font: inherit;
  margin: 0;
}
button {
  overflow: visible;
  text-transform: none;
  -webkit-appearance: button;
  cursor: pointer;
}
@supports (backdrop-filter: blur(20px)) {
  .blur {
    background: rgba(255,255,255,0.9) !important;
    backdrop-filter: saturate(200%) blur(20px);
  }
}
.shadow {
  box-shadow: 0 1px 2px 0px rgba(0,0,0,0.1);
  -webkit-box-shadow: 0 1px 2px 0px rgba(0,0,0,0.1);
}
.shadow.floatable {
  transition: all 0.28s ease;
  -webkit-transition: all 0.28s ease;
  -khtml-transition: all 0.28s ease;
  -moz-transition: all 0.28s ease;
  -o-transition: all 0.28s ease;
  -ms-transition: all 0.28s ease;
}
.shadow.floatable:hover {
  box-shadow: 0 2px 4px 0px rgba(0,0,0,0.1), 0 4px 8px 0px rgba(0,0,0,0.1), 0 8px 16px 0px rgba(0,0,0,0.1);
  -webkit-box-shadow: 0 2px 4px 0px rgba(0,0,0,0.1), 0 4px 8px 0px rgba(0,0,0,0.1), 0 8px 16px 0px rgba(0,0,0,0.1);
}
#l_cover {
  min-height: 64px;
}
.cover-wrapper {
  top: 0;
  left: 0;
  max-width: 100%;
  height: 100vh;
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: -ms-flexbox /* TWEENER - IE 10 */;
  display: -webkit-flex /* NEW - Chrome */;
  display: flex /* NEW, Spec - Opera 12.1, Firefox 20+ */;
  display: flex;
  flex-wrap: nowrap;
  -webkit-flex-wrap: nowrap;
  -khtml-flex-wrap: nowrap;
  -moz-flex-wrap: nowrap;
  -o-flex-wrap: nowrap;
  -ms-flex-wrap: nowrap;
  -webkit-box-direction: normal;
  -moz-box-direction: normal;
  -webkit-box-orient: vertical;
  -moz-box-orient: vertical;
  -webkit-flex-direction: column;
  -ms-flex-direction: column;
  flex-direction: column;
  align-items: center;
  align-self: center;
  align-content: center;
  color: var(--color-site-inner);
  padding: 0 16px;
  user-select: none;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  position: relative;
  overflow: hidden;
  margin-bottom: -100px;
}
.cover-wrapper .cover-bg {
  position: absolute;
  width: 100%;
  height: 100%;
  background-position: center;
  background-size: cover;
  -webkit-background-size: cover;
  -moz-background-size: cover;
}
.cover-wrapper .cover-bg.lazyload:not(.loaded) {
  opacity: 0;
  -webkit-opacity: 0;
  -moz-opacity: 0;
}
.cover-wrapper .cover-bg.lazyload.loaded {
  animation-delay: 0s;
  animation-duration: 0.5s;
  animation-fill-mode: forwards;
  animation-timing-function: ease-out;
  animation-name: fadeIn;
}
@-moz-keyframes fadeIn {
  0% {
    opacity: 0;
    -webkit-opacity: 0;
    -moz-opacity: 0;
    filter: blur(12px);
    transform: scale(1.02);
    -webkit-transform: scale(1.02);
    -khtml-transform: scale(1.02);
    -moz-transform: scale(1.02);
    -o-transform: scale(1.02);
    -ms-transform: scale(1.02);
  }
  100% {
    opacity: 1;
    -webkit-opacity: 1;
    -moz-opacity: 1;
  }
}
@-webkit-keyframes fadeIn {
  0% {
    opacity: 0;
    -webkit-opacity: 0;
    -moz-opacity: 0;
    filter: blur(12px);
    transform: scale(1.02);
    -webkit-transform: scale(1.02);
    -khtml-transform: scale(1.02);
    -moz-transform: scale(1.02);
    -o-transform: scale(1.02);
    -ms-transform: scale(1.02);
  }
  100% {
    opacity: 1;
    -webkit-opacity: 1;
    -moz-opacity: 1;
  }
}
@-o-keyframes fadeIn {
  0% {
    opacity: 0;
    -webkit-opacity: 0;
    -moz-opacity: 0;
    filter: blur(12px);
    transform: scale(1.02);
    -webkit-transform: scale(1.02);
    -khtml-transform: scale(1.02);
    -moz-transform: scale(1.02);
    -o-transform: scale(1.02);
    -ms-transform: scale(1.02);
  }
  100% {
    opacity: 1;
    -webkit-opacity: 1;
    -moz-opacity: 1;
  }
}
@keyframes fadeIn {
  0% {
    opacity: 0;
    -webkit-opacity: 0;
    -moz-opacity: 0;
    filter: blur(12px);
    transform: scale(1.02);
    -webkit-transform: scale(1.02);
    -khtml-transform: scale(1.02);
    -moz-transform: scale(1.02);
    -o-transform: scale(1.02);
    -ms-transform: scale(1.02);
  }
  100% {
    opacity: 1;
    -webkit-opacity: 1;
    -moz-opacity: 1;
  }
}
.cover-wrapper .cover-body {
  z-index: 1;
  position: relative;
  width: 100%;
  height: 100%;
}
.cover-wrapper#full {
  height: calc(100vh + 100px);
  padding-bottom: 100px;
}
.cover-wrapper#half {
  max-height: 640px;
  min-height: 400px;
  height: calc(36vh - 64px + 200px);
}
.cover-wrapper #scroll-down {
  width: 100%;
  height: 64px;
  position: absolute;
  bottom: 100px;
  text-align: center;
  cursor: pointer;
}
.cover-wrapper #scroll-down .scroll-down-effects {
  color: #fff;
  font-size: 24px;
  line-height: 64px;
  position: absolute;
  width: 24px;
  left: calc(50% - 12px);
  text-shadow: 0 1px 2px rgba(0,0,0,0.1);
  animation: scroll-down-effect 1.5s infinite;
  -webkit-animation: scroll-down-effect 1.5s infinite;
  -khtml-animation: scroll-down-effect 1.5s infinite;
  -moz-animation: scroll-down-effect 1.5s infinite;
  -o-animation: scroll-down-effect 1.5s infinite;
  -ms-animation: scroll-down-effect 1.5s infinite;
}
@-moz-keyframes scroll-down-effect {
  0% {
    top: 0;
    opacity: 1;
    -webkit-opacity: 1;
    -moz-opacity: 1;
  }
  50% {
    top: -16px;
    opacity: 0.4;
    -webkit-opacity: 0.4;
    -moz-opacity: 0.4;
  }
  100% {
    top: 0;
    opacity: 1;
    -webkit-opacity: 1;
    -moz-opacity: 1;
  }
}
@-webkit-keyframes scroll-down-effect {
  0% {
    top: 0;
    opacity: 1;
    -webkit-opacity: 1;
    -moz-opacity: 1;
  }
  50% {
    top: -16px;
    opacity: 0.4;
    -webkit-opacity: 0.4;
    -moz-opacity: 0.4;
  }
  100% {
    top: 0;
    opacity: 1;
    -webkit-opacity: 1;
    -moz-opacity: 1;
  }
}
@-o-keyframes scroll-down-effect {
  0% {
    top: 0;
    opacity: 1;
    -webkit-opacity: 1;
    -moz-opacity: 1;
  }
  50% {
    top: -16px;
    opacity: 0.4;
    -webkit-opacity: 0.4;
    -moz-opacity: 0.4;
  }
  100% {
    top: 0;
    opacity: 1;
    -webkit-opacity: 1;
    -moz-opacity: 1;
  }
}
@keyframes scroll-down-effect {
  0% {
    top: 0;
    opacity: 1;
    -webkit-opacity: 1;
    -moz-opacity: 1;
  }
  50% {
    top: -16px;
    opacity: 0.4;
    -webkit-opacity: 0.4;
    -moz-opacity: 0.4;
  }
  100% {
    top: 0;
    opacity: 1;
    -webkit-opacity: 1;
    -moz-opacity: 1;
  }
}
.cover-wrapper .cover-body {
  margin-top: 64px;
  margin-bottom: 100px;
}
.cover-wrapper .cover-body,
.cover-wrapper .cover-body .top,
.cover-wrapper .cover-body .bottom {
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: -ms-flexbox /* TWEENER - IE 10 */;
  display: -webkit-flex /* NEW - Chrome */;
  display: flex /* NEW, Spec - Opera 12.1, Firefox 20+ */;
  display: flex;
  -webkit-box-direction: normal;
  -moz-box-direction: normal;
  -webkit-box-orient: vertical;
  -moz-box-orient: vertical;
  -webkit-flex-direction: column;
  -ms-flex-direction: column;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  -webkit-justify-content: center;
  -khtml-justify-content: center;
  -moz-justify-content: center;
  -o-justify-content: center;
  -ms-justify-content: center;
  max-width: 100%;
}
.cover-wrapper .cover-body .bottom {
  margin-top: 32px;
}
.cover-wrapper .cover-body .title {
  font-family: "Varela Round", "PingFang SC", "Microsoft YaHei", Helvetica, Arial, Helvetica, monospace;
  font-size: 3.125rem;
  line-height: 1.2;
  text-shadow: 0 1px 2px rgba(0,0,0,0.1);
}
.cover-wrapper .cover-body .subtitle {
  font-size: 20px;
}
.cover-wrapper .cover-body .logo {
  max-height: 120px;
  max-width: calc(100% - 4 * 16px);
}
@media screen and (min-height: 1024px) {
  .cover-wrapper .cover-body .title {
    font-size: 3rem;
  }
  .cover-wrapper .cover-body .subtitle {
    font-size: 1.05rem;
  }
  .cover-wrapper .cover-body .logo {
    max-height: 150px;
  }
}
.cover-wrapper .cover-body .m_search {
  position: relative;
  max-width: calc(100% - 16px);
  width: 320px;
  vertical-align: middle;
}
.cover-wrapper .cover-body .m_search .form {
  position: relative;
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: block;
  width: 100%;
}
.cover-wrapper .cover-body .m_search .icon,
.cover-wrapper .cover-body .m_search .input {
  transition: all 0.28s ease;
  -webkit-transition: all 0.28s ease;
  -khtml-transition: all 0.28s ease;
  -moz-transition: all 0.28s ease;
  -o-transition: all 0.28s ease;
  -ms-transition: all 0.28s ease;
}
.cover-wrapper .cover-body .m_search .icon {
  position: absolute;
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: block;
  line-height: 2.5rem;
  width: 32px;
  top: 0;
  left: 5px;
  color: rgba(68,68,68,0.75);
}
.cover-wrapper .cover-body .m_search .input {
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: block;
  height: 2.5rem;
  width: 100%;
  box-shadow: none;
  -webkit-box-shadow: none;
  box-sizing: border-box;
  -webkit-box-sizing: border-box;
  -moz-box-sizing: border-box;
  font-size: 0.875rem;
  -webkit-appearance: none;
  padding-left: 36px;
  border-radius: 1.4rem;
  -webkit-border-radius: 1.4rem;
  background: rgba(255,255,255,0.6);
  backdrop-filter: blur(10px);
  border: none;
  color: var(--color-text);
}
@media screen and (max-width: 500px) {
  .cover-wrapper .cover-body .m_search .input {
    padding-left: 36px;
  }
}
.cover-wrapper .cover-body .m_search .input:hover {
  background: rgba(255,255,255,0.8);
}
.cover-wrapper .cover-body .m_search .input:focus {
  background: #fff;
}
.cover-wrapper .list-h {
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: -ms-flexbox /* TWEENER - IE 10 */;
  display: -webkit-flex /* NEW - Chrome */;
  display: flex /* NEW, Spec - Opera 12.1, Firefox 20+ */;
  display: flex;
  -webkit-box-direction: normal;
  -moz-box-direction: normal;
  -webkit-box-orient: horizontal;
  -moz-box-orient: horizontal;
  -webkit-flex-direction: row;
  -ms-flex-direction: row;
  flex-direction: row;
  flex-wrap: wrap;
  -webkit-flex-wrap: wrap;
  -khtml-flex-wrap: wrap;
  -moz-flex-wrap: wrap;
  -o-flex-wrap: wrap;
  -ms-flex-wrap: wrap;
  align-items: stretch;
  border-radius: 4px;
  -webkit-border-radius: 4px;
  user-select: none;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
}
.cover-wrapper .list-h a {
  -webkit-box-flex: 1;
  -moz-box-flex: 1;
  -webkit-flex: 1 0;
  -ms-flex: 1 0;
  flex: 1 0;
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: -ms-flexbox /* TWEENER - IE 10 */;
  display: -webkit-flex /* NEW - Chrome */;
  display: flex /* NEW, Spec - Opera 12.1, Firefox 20+ */;
  display: flex;
  font-weight: 600;
}
.cover-wrapper .list-h a img {
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: block;
  border-radius: 2px;
  -webkit-border-radius: 2px;
  margin: 4px;
  min-width: 40px;
  max-width: 44px;
}
@media screen and (max-width: 768px) {
  .cover-wrapper .list-h a img {
    min-width: 36px;
    max-width: 40px;
  }
}
@media screen and (max-width: 500px) {
  .cover-wrapper .list-h a img {
    margin: 2px 4px;
    min-width: 32px;
    max-width: 36px;
  }
}
@media screen and (max-width: 375px) {
  .cover-wrapper .list-h a img {
    min-width: 28px;
    max-width: 32px;
  }
}
.cover-wrapper {
  max-width: 100%;
}
.cover-wrapper.search .bottom .menu {
  margin-top: 16px;
}
.cover-wrapper.search .bottom .menu .list-h a {
  white-space: nowrap;
  -webkit-box-direction: normal;
  -moz-box-direction: normal;
  -webkit-box-orient: horizontal;
  -moz-box-orient: horizontal;
  -webkit-flex-direction: row;
  -ms-flex-direction: row;
  flex-direction: row;
  align-items: baseline;
  padding: 2px;
  margin: 4px;
  color: var(--color-site-inner);
  opacity: 0.75;
  -webkit-opacity: 0.75;
  -moz-opacity: 0.75;
  text-shadow: 0 1px 2px rgba(0,0,0,0.05);
  border-bottom: 2px solid transparent;
}
.cover-wrapper.search .bottom .menu .list-h a i {
  margin-right: 4px;
}
.cover-wrapper.search .bottom .menu .list-h a p {
  font-size: 0.9375rem;
}
.cover-wrapper.search .bottom .menu .list-h a:hover,
.cover-wrapper.search .bottom .menu .list-h a.active,
.cover-wrapper.search .bottom .menu .list-h a:active {
  opacity: 1;
  -webkit-opacity: 1;
  -moz-opacity: 1;
  border-bottom: 2px solid var(--color-site-inner);
}
.cover-wrapper.dock .menu,
.cover-wrapper.featured .menu,
.cover-wrapper.focus .menu {
  border-radius: 6px;
  -webkit-border-radius: 6px;
}
.cover-wrapper.dock .menu .list-h a,
.cover-wrapper.featured .menu .list-h a,
.cover-wrapper.focus .menu .list-h a {
  -webkit-box-direction: normal;
  -moz-box-direction: normal;
  -webkit-box-orient: vertical;
  -moz-box-orient: vertical;
  -webkit-flex-direction: column;
  -ms-flex-direction: column;
  flex-direction: column;
  align-items: center;
  padding: 12px;
  line-height: 24px;
  border-radius: 4px;
  -webkit-border-radius: 4px;
  border-bottom: none;
  text-align: center;
  align-content: flex-end;
  color: rgba(68,68,68,0.7);
  font-size: 1.5rem;
}
@media screen and (max-width: 500px) {
  .cover-wrapper.dock .menu .list-h a,
  .cover-wrapper.featured .menu .list-h a,
  .cover-wrapper.focus .menu .list-h a {
    padding: 12px 8px;
  }
}
.cover-wrapper.dock .menu .list-h a i,
.cover-wrapper.featured .menu .list-h a i,
.cover-wrapper.focus .menu .list-h a i {
  margin: 8px;
}
.cover-wrapper.dock .menu .list-h a p,
.cover-wrapper.featured .menu .list-h a p,
.cover-wrapper.focus .menu .list-h a p {
  font-size: 0.875rem;
}
.cover-wrapper.dock .menu .list-h a.active,
.cover-wrapper.featured .menu .list-h a.active,
.cover-wrapper.focus .menu .list-h a.active {
  background: var(--color-card);
  backdrop-filter: none;
}
.cover-wrapper.dock .menu .list-h a.active i,
.cover-wrapper.featured .menu .list-h a.active i,
.cover-wrapper.focus .menu .list-h a.active i,
.cover-wrapper.dock .menu .list-h a.active i+p,
.cover-wrapper.featured .menu .list-h a.active i+p,
.cover-wrapper.focus .menu .list-h a.active i+p {
  color: #3dd9b6;
}
.cover-wrapper.dock .menu .list-h a.active img+p,
.cover-wrapper.featured .menu .list-h a.active img+p,
.cover-wrapper.focus .menu .list-h a.active img+p {
  color: var(--color-text);
}
.cover-wrapper.dock .menu .list-h a:hover,
.cover-wrapper.featured .menu .list-h a:hover,
.cover-wrapper.focus .menu .list-h a:hover {
  background: var(--color-card);
}
.cover-wrapper.dock .top {
  margin-bottom: 48px;
}
.cover-wrapper.dock .menu {
  background: rgba(255,255,255,0.5);
  position: absolute;
  bottom: 0;
  max-width: 100%;
}
.cover-wrapper.dock .menu .list-h {
  flex-wrap: nowrap;
  -webkit-flex-wrap: nowrap;
  -khtml-flex-wrap: nowrap;
  -moz-flex-wrap: nowrap;
  -o-flex-wrap: nowrap;
  -ms-flex-wrap: nowrap;
  margin: 4px;
}
.cover-wrapper.dock .menu .list-h a+a {
  margin-left: 4px;
}
@media screen and (max-width: 500px) {
  .cover-wrapper.dock .menu .list-h {
    overflow-x: scroll;
  }
  .cover-wrapper.dock .menu .list-h::-webkit-scrollbar {
    height: 0;
    width: 0;
  }
  .cover-wrapper.dock .menu .list-h::-webkit-scrollbar-track-piece {
    background: transparent;
  }
  .cover-wrapper.dock .menu .list-h::-webkit-scrollbar-thumb {
    background: #3dd9b6;
    cursor: pointer;
    border-radius: 0;
    -webkit-border-radius: 0;
  }
  .cover-wrapper.dock .menu .list-h::-webkit-scrollbar-thumb:hover {
    background: #ff5722;
  }
}
@supports (backdrop-filter: blur(20px)) {
  .cover-wrapper.dock .menu {
    background: rgba(255,255,255,0.5);
    backdrop-filter: saturate(200%) blur(20px);
  }
}
@font-face {
  font-family: 'UbuntuMono';
  src: url("https://unpkg.com/volantis-static@0.0.1654736714924/media/fonts/UbuntuMono/UbuntuMono-Regular.ttf");
  font-weight: 'normal';
  font-style: 'normal';
  font-display: swap;
}
@font-face {
  font-family: 'Varela Round';
  src: url("https://unpkg.com/volantis-static@0.0.1654736714924/media/fonts/VarelaRound/VarelaRound-Regular.ttf");
  font-weight: 'normal';
  font-style: 'normal';
  font-display: swap;
}
.l_header {
  position: fixed;
  z-index: 1000;
  top: 0;
  width: 100%;
  height: 64px;
  background: var(--color-card);
  box-shadow: 0 1px 2px 0px rgba(0,0,0,0.1);
  -webkit-box-shadow: 0 1px 2px 0px rgba(0,0,0,0.1);
}
.l_header.auto {
  transition: opacity 0.4s ease;
  -webkit-transition: opacity 0.4s ease;
  -khtml-transition: opacity 0.4s ease;
  -moz-transition: opacity 0.4s ease;
  -o-transition: opacity 0.4s ease;
  -ms-transition: opacity 0.4s ease;
  visibility: hidden;
}
.l_header.auto.show {
  opacity: 1 !important;
  -webkit-opacity: 1 !important;
  -moz-opacity: 1 !important;
  visibility: visible;
}
.l_header .container {
  margin-left: 16px;
  margin-right: 16px;
}
.l_header #wrapper {
  height: 100%;
  user-select: none;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
}
.l_header #wrapper .nav-main,
.l_header #wrapper .nav-sub {
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: -ms-flexbox /* TWEENER - IE 10 */;
  display: -webkit-flex /* NEW - Chrome */;
  display: flex /* NEW, Spec - Opera 12.1, Firefox 20+ */;
  display: flex;
  flex-wrap: nowrap;
  -webkit-flex-wrap: nowrap;
  -khtml-flex-wrap: nowrap;
  -moz-flex-wrap: nowrap;
  -o-flex-wrap: nowrap;
  -ms-flex-wrap: nowrap;
  justify-content: space-between;
  -webkit-justify-content: space-between;
  -khtml-justify-content: space-between;
  -moz-justify-content: space-between;
  -o-justify-content: space-between;
  -ms-justify-content: space-between;
  align-items: center;
}
.l_header #wrapper .nav-main {
  transition: all 0.28s ease;
  -webkit-transition: all 0.28s ease;
  -khtml-transition: all 0.28s ease;
  -moz-transition: all 0.28s ease;
  -o-transition: all 0.28s ease;
  -ms-transition: all 0.28s ease;
}
.l_header #wrapper.sub .nav-main {
  transform: translateY(-64px);
  -webkit-transform: translateY(-64px);
  -khtml-transform: translateY(-64px);
  -moz-transform: translateY(-64px);
  -o-transform: translateY(-64px);
  -ms-transform: translateY(-64px);
}
.l_header #wrapper .nav-sub {
  transition: all 0.28s ease;
  -webkit-transition: all 0.28s ease;
  -khtml-transition: all 0.28s ease;
  -moz-transition: all 0.28s ease;
  -o-transition: all 0.28s ease;
  -ms-transition: all 0.28s ease;
  opacity: 0;
  -webkit-opacity: 0;
  -moz-opacity: 0;
  height: 64px;
  width: calc(100% - 2 * 16px);
  position: absolute;
}
.l_header #wrapper .nav-sub ::-webkit-scrollbar {
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: none;
}
@media screen and (min-width: 2048px) {
  .l_header #wrapper .nav-sub {
    max-width: 55vw;
    margin: auto;
  }
}
.l_header #wrapper.sub .nav-sub {
  opacity: 1;
  -webkit-opacity: 1;
  -moz-opacity: 1;
}
.l_header #wrapper .title {
  position: relative;
  color: var(--color-text);
  padding-left: 24px;
  max-height: 64px;
}
.l_header #wrapper .nav-main .title {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  flex-shrink: 0;
  line-height: 64px;
  padding: 0 24px;
  font-size: 1.25rem;
  font-family: "Varela Round", "PingFang SC", "Microsoft YaHei", Helvetica, Arial, Helvetica, monospace;
}
.l_header #wrapper .nav-main .title img {
  height: 64px;
}
.l_header .nav-sub {
  max-width: 1080px;
  margin: auto;
}
.l_header .nav-sub .title {
  font-weight: bold;
  font-family: UbuntuMono, "Varela Round", "PingFang SC", "Microsoft YaHei", Helvetica, Arial, Menlo, Monaco, monospace, sans-serif;
  line-height: 1.2;
  max-height: 64px;
  white-space: normal;
  flex-shrink: 1;
}
.l_header .switcher {
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: none;
  line-height: 64px;
  align-items: center;
}
.l_header .switcher .s-toc {
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: none;
}
@media screen and (max-width: 768px) {
  .l_header .switcher .s-toc {
    display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
    display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
    display: -ms-flexbox /* TWEENER - IE 10 */;
    display: -webkit-flex /* NEW - Chrome */;
    display: flex /* NEW, Spec - Opera 12.1, Firefox 20+ */;
    display: flex;
  }
}
.l_header .switcher >li {
  height: 48px;
  transition: all 0.28s ease;
  -webkit-transition: all 0.28s ease;
  -khtml-transition: all 0.28s ease;
  -moz-transition: all 0.28s ease;
  -o-transition: all 0.28s ease;
  -ms-transition: all 0.28s ease;
  margin: 2px;
}
@media screen and (max-width: 500px) {
  .l_header .switcher >li {
    margin: 0 1px;
    height: 48px;
  }
}
.l_header .switcher >li >a {
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: -ms-flexbox /* TWEENER - IE 10 */;
  display: -webkit-flex /* NEW - Chrome */;
  display: flex /* NEW, Spec - Opera 12.1, Firefox 20+ */;
  display: flex;
  justify-content: center;
  -webkit-justify-content: center;
  -khtml-justify-content: center;
  -moz-justify-content: center;
  -o-justify-content: center;
  -ms-justify-content: center;
  align-items: center;
  width: 48px;
  height: 48px;
  padding: 0.85em 1.1em;
  border-radius: 100px;
  -webkit-border-radius: 100px;
  border: none;
  transition: all 0.28s ease;
  -webkit-transition: all 0.28s ease;
  -khtml-transition: all 0.28s ease;
  -moz-transition: all 0.28s ease;
  -o-transition: all 0.28s ease;
  -ms-transition: all 0.28s ease;
  color: #3dd9b6;
}
.l_header .switcher >li >a:hover {
  border: none;
}
.l_header .switcher >li >a.active,
.l_header .switcher >li >a:active {
  border: none;
  background: var(--color-site-bg);
}
@media screen and (max-width: 500px) {
  .l_header .switcher >li >a {
    width: 36px;
    height: 48px;
  }
}
.l_header .nav-sub .switcher {
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: -ms-flexbox /* TWEENER - IE 10 */;
  display: -webkit-flex /* NEW - Chrome */;
  display: flex /* NEW, Spec - Opera 12.1, Firefox 20+ */;
  display: flex;
}
.l_header .m_search {
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: -ms-flexbox /* TWEENER - IE 10 */;
  display: -webkit-flex /* NEW - Chrome */;
  display: flex /* NEW, Spec - Opera 12.1, Firefox 20+ */;
  display: flex;
  height: 64px;
  width: 240px;
  transition: all 0.28s ease;
  -webkit-transition: all 0.28s ease;
  -khtml-transition: all 0.28s ease;
  -moz-transition: all 0.28s ease;
  -o-transition: all 0.28s ease;
  -ms-transition: all 0.28s ease;
}
@media screen and (max-width: 1024px) {
  .l_header .m_search {
    width: 44px;
    min-width: 44px;
  }
  .l_header .m_search input::placeholder {
    opacity: 0;
    -webkit-opacity: 0;
    -moz-opacity: 0;
  }
  .l_header .m_search:hover {
    width: 240px;
  }
  .l_header .m_search:hover input::placeholder {
    opacity: 1;
    -webkit-opacity: 1;
    -moz-opacity: 1;
  }
}
@media screen and (min-width: 500px) {
  .l_header .m_search:hover .input {
    width: 100%;
  }
  .l_header .m_search:hover .input::placeholder {
    opacity: 1;
    -webkit-opacity: 1;
    -moz-opacity: 1;
  }
}
@media screen and (max-width: 500px) {
  .l_header .m_search {
    min-width: 0;
  }
  .l_header .m_search input::placeholder {
    opacity: 1;
    -webkit-opacity: 1;
    -moz-opacity: 1;
  }
}
.l_header .m_search .form {
  position: relative;
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: -ms-flexbox /* TWEENER - IE 10 */;
  display: -webkit-flex /* NEW - Chrome */;
  display: flex /* NEW, Spec - Opera 12.1, Firefox 20+ */;
  display: flex;
  width: 100%;
  align-items: center;
}
.l_header .m_search .icon {
  position: absolute;
  width: 36px;
  left: 5px;
  color: var(--color-meta);
}
@media screen and (max-width: 500px) {
  .l_header .m_search .icon {
    display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
    display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
    display: none;
  }
}
.l_header .m_search .input {
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: block;
  padding-top: 8px;
  padding-bottom: 8px;
  line-height: 1.3;
  width: 100%;
  color: var(--color-text);
  background: #fafafa;
  box-shadow: none;
  -webkit-box-shadow: none;
  box-sizing: border-box;
  -webkit-box-sizing: border-box;
  -moz-box-sizing: border-box;
  padding-left: 40px;
  font-size: 0.875rem;
  border-radius: 8px;
  -webkit-border-radius: 8px;
  border: none;
  transition: all 0.28s ease;
  -webkit-transition: all 0.28s ease;
  -khtml-transition: all 0.28s ease;
  -moz-transition: all 0.28s ease;
  -o-transition: all 0.28s ease;
  -ms-transition: all 0.28s ease;
}
@media screen and (min-width: 500px) {
  .l_header .m_search .input:focus {
    box-shadow: 0 4px 8px 0px rgba(0,0,0,0.1);
    -webkit-box-shadow: 0 4px 8px 0px rgba(0,0,0,0.1);
  }
}
@media screen and (max-width: 500px) {
  .l_header .m_search .input {
    background: var(--color-block);
    padding-left: 8px;
    border: none;
  }
  .l_header .m_search .input:hover,
  .l_header .m_search .input:focus {
    border: none;
  }
}
@media (max-width: 500px) {
  .l_header .m_search {
    left: 0;
    width: 0;
    overflow: hidden;
    position: absolute;
    background: #fff;
    transition: all 0.28s ease;
    -webkit-transition: all 0.28s ease;
    -khtml-transition: all 0.28s ease;
    -moz-transition: all 0.28s ease;
    -o-transition: all 0.28s ease;
    -ms-transition: all 0.28s ease;
  }
  .l_header .m_search .input {
    border-radius: 32px;
    -webkit-border-radius: 32px;
    margin-left: 16px;
    padding-left: 16px;
  }
  .l_header.z_search-open .m_search {
    width: 100%;
  }
  .l_header.z_search-open .m_search .input {
    width: calc(100% - 120px);
  }
}
ul.m-pc >li>a {
  color: inherit;
  border-bottom: 2px solid transparent;
}
ul.m-pc >li>a:active,
ul.m-pc >li>a.active {
  border-bottom: 2px solid #3dd9b6;
}
ul.m-pc li:hover >ul.list-v,
ul.list-v li:hover >ul.list-v {
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: block;
}
ul.nav-list-h {
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: -ms-flexbox /* TWEENER - IE 10 */;
  display: -webkit-flex /* NEW - Chrome */;
  display: flex /* NEW, Spec - Opera 12.1, Firefox 20+ */;
  display: flex;
  align-items: stretch;
}
ul.nav-list-h>li {
  position: relative;
  justify-content: center;
  -webkit-justify-content: center;
  -khtml-justify-content: center;
  -moz-justify-content: center;
  -o-justify-content: center;
  -ms-justify-content: center;
  height: 100%;
  line-height: 2.4;
  border-radius: 4px;
  -webkit-border-radius: 4px;
}
ul.nav-list-h>li >a {
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  font-weight: 600;
}
ul.list-v {
  z-index: 1;
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: none;
  position: absolute;
  background: var(--color-card);
  box-shadow: 0 2px 4px 0px rgba(0,0,0,0.08), 0 4px 8px 0px rgba(0,0,0,0.08), 0 8px 16px 0px rgba(0,0,0,0.08);
  -webkit-box-shadow: 0 2px 4px 0px rgba(0,0,0,0.08), 0 4px 8px 0px rgba(0,0,0,0.08), 0 8px 16px 0px rgba(0,0,0,0.08);
  margin-top: -6px;
  border-radius: 4px;
  -webkit-border-radius: 4px;
  padding: 8px 0;
}
ul.list-v.show {
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: block;
}
ul.list-v hr {
  margin-top: 8px;
  margin-bottom: 8px;
}
ul.list-v >li {
  white-space: nowrap;
  word-break: keep-all;
}
ul.list-v >li.header {
  font-size: 0.78125rem;
  font-weight: bold;
  line-height: 2em;
  color: var(--color-meta);
  margin: 8px 16px 4px;
}
ul.list-v >li.header i {
  margin-right: 8px;
}
ul.list-v >li ul {
  margin-left: 0;
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: none;
  margin-top: -40px;
}
ul.list-v .aplayer-container {
  min-height: 64px;
  padding: 6px 16px;
}
ul.list-v >li>a {
  transition: all 0.28s ease;
  -webkit-transition: all 0.28s ease;
  -khtml-transition: all 0.28s ease;
  -moz-transition: all 0.28s ease;
  -o-transition: all 0.28s ease;
  -ms-transition: all 0.28s ease;
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: block;
  color: var(--color-list);
  font-size: 0.875rem;
  font-weight: bold;
  line-height: 36px;
  padding: 0 20px 0 16px;
  text-overflow: ellipsis;
  margin: 0 4px;
  border-radius: 4px;
  -webkit-border-radius: 4px;
}
@media screen and (max-width: 1024px) {
  ul.list-v >li>a {
    line-height: 40px;
  }
}
ul.list-v >li>a >i {
  margin-right: 8px;
}
ul.list-v >li>a:active,
ul.list-v >li>a.active {
  color: var(--color-list-hl);
}
ul.list-v >li>a:hover {
  color: var(--color-list-hl);
  background: var(--color-site-bg);
}
.l_header .menu >ul>li>a {
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: block;
  padding: 0 8px;
}
.l_header .menu >ul>li>a >i {
  margin-right: 4px;
}
.l_header ul.nav-list-h>li {
  color: var(--color-list);
  line-height: 64px;
}
.l_header ul.nav-list-h>li >a {
  max-height: 64px;
  overflow: hidden;
  color: inherit;
}
.l_header ul.nav-list-h>li >a:active,
.l_header ul.nav-list-h>li >a.active {
  color: #3dd9b6;
}
.l_header ul.nav-list-h>li:hover>a {
  color: var(--color-list-hl);
}
.l_header ul.nav-list-h>li i.music {
  animation: rotate-effect 1.5s linear infinite;
  -webkit-animation: rotate-effect 1.5s linear infinite;
  -khtml-animation: rotate-effect 1.5s linear infinite;
  -moz-animation: rotate-effect 1.5s linear infinite;
  -o-animation: rotate-effect 1.5s linear infinite;
  -ms-animation: rotate-effect 1.5s linear infinite;
}
@-moz-keyframes rotate-effect {
  0% {
    transform: rotate(0);
    -webkit-transform: rotate(0);
    -khtml-transform: rotate(0);
    -moz-transform: rotate(0);
    -o-transform: rotate(0);
    -ms-transform: rotate(0);
  }
  25% {
    transform: rotate(90deg);
    -webkit-transform: rotate(90deg);
    -khtml-transform: rotate(90deg);
    -moz-transform: rotate(90deg);
    -o-transform: rotate(90deg);
    -ms-transform: rotate(90deg);
  }
  50% {
    transform: rotate(180deg);
    -webkit-transform: rotate(180deg);
    -khtml-transform: rotate(180deg);
    -moz-transform: rotate(180deg);
    -o-transform: rotate(180deg);
    -ms-transform: rotate(180deg);
  }
  75% {
    transform: rotate(270deg);
    -webkit-transform: rotate(270deg);
    -khtml-transform: rotate(270deg);
    -moz-transform: rotate(270deg);
    -o-transform: rotate(270deg);
    -ms-transform: rotate(270deg);
  }
  100% {
    transform: rotate(360deg);
    -webkit-transform: rotate(360deg);
    -khtml-transform: rotate(360deg);
    -moz-transform: rotate(360deg);
    -o-transform: rotate(360deg);
    -ms-transform: rotate(360deg);
  }
}
@-webkit-keyframes rotate-effect {
  0% {
    transform: rotate(0);
    -webkit-transform: rotate(0);
    -khtml-transform: rotate(0);
    -moz-transform: rotate(0);
    -o-transform: rotate(0);
    -ms-transform: rotate(0);
  }
  25% {
    transform: rotate(90deg);
    -webkit-transform: rotate(90deg);
    -khtml-transform: rotate(90deg);
    -moz-transform: rotate(90deg);
    -o-transform: rotate(90deg);
    -ms-transform: rotate(90deg);
  }
  50% {
    transform: rotate(180deg);
    -webkit-transform: rotate(180deg);
    -khtml-transform: rotate(180deg);
    -moz-transform: rotate(180deg);
    -o-transform: rotate(180deg);
    -ms-transform: rotate(180deg);
  }
  75% {
    transform: rotate(270deg);
    -webkit-transform: rotate(270deg);
    -khtml-transform: rotate(270deg);
    -moz-transform: rotate(270deg);
    -o-transform: rotate(270deg);
    -ms-transform: rotate(270deg);
  }
  100% {
    transform: rotate(360deg);
    -webkit-transform: rotate(360deg);
    -khtml-transform: rotate(360deg);
    -moz-transform: rotate(360deg);
    -o-transform: rotate(360deg);
    -ms-transform: rotate(360deg);
  }
}
@-o-keyframes rotate-effect {
  0% {
    transform: rotate(0);
    -webkit-transform: rotate(0);
    -khtml-transform: rotate(0);
    -moz-transform: rotate(0);
    -o-transform: rotate(0);
    -ms-transform: rotate(0);
  }
  25% {
    transform: rotate(90deg);
    -webkit-transform: rotate(90deg);
    -khtml-transform: rotate(90deg);
    -moz-transform: rotate(90deg);
    -o-transform: rotate(90deg);
    -ms-transform: rotate(90deg);
  }
  50% {
    transform: rotate(180deg);
    -webkit-transform: rotate(180deg);
    -khtml-transform: rotate(180deg);
    -moz-transform: rotate(180deg);
    -o-transform: rotate(180deg);
    -ms-transform: rotate(180deg);
  }
  75% {
    transform: rotate(270deg);
    -webkit-transform: rotate(270deg);
    -khtml-transform: rotate(270deg);
    -moz-transform: rotate(270deg);
    -o-transform: rotate(270deg);
    -ms-transform: rotate(270deg);
  }
  100% {
    transform: rotate(360deg);
    -webkit-transform: rotate(360deg);
    -khtml-transform: rotate(360deg);
    -moz-transform: rotate(360deg);
    -o-transform: rotate(360deg);
    -ms-transform: rotate(360deg);
  }
}
@keyframes rotate-effect {
  0% {
    transform: rotate(0);
    -webkit-transform: rotate(0);
    -khtml-transform: rotate(0);
    -moz-transform: rotate(0);
    -o-transform: rotate(0);
    -ms-transform: rotate(0);
  }
  25% {
    transform: rotate(90deg);
    -webkit-transform: rotate(90deg);
    -khtml-transform: rotate(90deg);
    -moz-transform: rotate(90deg);
    -o-transform: rotate(90deg);
    -ms-transform: rotate(90deg);
  }
  50% {
    transform: rotate(180deg);
    -webkit-transform: rotate(180deg);
    -khtml-transform: rotate(180deg);
    -moz-transform: rotate(180deg);
    -o-transform: rotate(180deg);
    -ms-transform: rotate(180deg);
  }
  75% {
    transform: rotate(270deg);
    -webkit-transform: rotate(270deg);
    -khtml-transform: rotate(270deg);
    -moz-transform: rotate(270deg);
    -o-transform: rotate(270deg);
    -ms-transform: rotate(270deg);
  }
  100% {
    transform: rotate(360deg);
    -webkit-transform: rotate(360deg);
    -khtml-transform: rotate(360deg);
    -moz-transform: rotate(360deg);
    -o-transform: rotate(360deg);
    -ms-transform: rotate(360deg);
  }
}
.menu-phone li ul.list-v {
  right: calc(100% - 0.5 * 16px);
}
.menu-phone li ul.list-v ul {
  right: calc(100% - 0.5 * 16px);
}
#wrapper {
  max-width: 1080px;
  margin: auto;
}
@media screen and (min-width: 2048px) {
  #wrapper {
    max-width: 55vw;
  }
}
#wrapper .menu {
  -webkit-box-flex: 1;
  -moz-box-flex: 1;
  -webkit-flex: 1 1;
  -ms-flex: 1 1;
  flex: 1 1;
  margin: 0 16px 0 0;
}
#wrapper .menu .list-v ul {
  left: calc(100% - 0.5 * 16px);
}
.menu-phone {
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: none;
  margin-top: 16px;
  right: 8px;
  transition: all 0.28s ease;
  -webkit-transition: all 0.28s ease;
  -khtml-transition: all 0.28s ease;
  -moz-transition: all 0.28s ease;
  -o-transition: all 0.28s ease;
  -ms-transition: all 0.28s ease;
}
.menu-phone ul {
  right: calc(100% - 0.5 * 16px);
}
@media screen and (max-width: 500px) {
  .menu-phone {
    display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
    display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
    display: block;
  }
}
.l_header {
  max-width: 65vw;
  left: calc((100% - 65vw) * 0.5);
  border-bottom-left-radius: 8px;
  border-bottom-right-radius: 8px;
}
@media screen and (max-width: 2048px) {
  .l_header {
    max-width: 1112px;
    left: calc((100% - 1112px) * 0.5);
  }
}
@media screen and (max-width: 1112px) {
  .l_header {
    left: 0;
    border-radius: 0;
    -webkit-border-radius: 0;
    max-width: 100%;
  }
}
@media screen and (max-width: 500px) {
  .l_header .container {
    margin-left: 0;
    margin-right: 0;
  }
  .l_header #wrapper .nav-main .title {
    padding-left: 16px;
    padding-right: 16px;
  }
  .l_header #wrapper .nav-sub {
    width: 100%;
  }
  .l_header #wrapper .nav-sub .title {
    overflow-y: scroll;
    margin-top: 2px;
    padding: 8px 16px;
  }
  .l_header #wrapper .switcher {
    display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
    display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
    display: -ms-flexbox /* TWEENER - IE 10 */;
    display: -webkit-flex /* NEW - Chrome */;
    display: flex /* NEW, Spec - Opera 12.1, Firefox 20+ */;
    display: flex;
    margin-right: 8px;
  }
  .l_header .menu {
    display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
    display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
    display: none;
  }
}
@media screen and (max-width: 500px) {
  .list-v li {
    max-width: 270px;
  }
}
#u-search {
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: none;
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  padding: 60px 20px;
  z-index: 1001;
}
@media screen and (max-width: 680px) {
  #u-search {
    padding: 0px;
  }
}

  </style>
  <link rel="stylesheet" href="/css/style.css" media="print" onload="this.media='all';this.onload=null">
  <noscript><link rel="stylesheet" href="/css/style.css"></noscript>
  
<script>
if (/*@cc_on!@*/false || (!!window.MSInputMethodContext && !!document.documentMode))
    document.write(
	'<style>'+
		'html{'+
			'overflow-x: hidden !important;'+
			'overflow-y: hidden !important;'+
		'}'+
		'.kill-ie{'+
			'text-align:center;'+
			'height: 100%;'+
			'margin-top: 15%;'+
			'margin-bottom: 5500%;'+
		'}'+
    '.kill-t{'+
      'font-size: 2rem;'+
    '}'+
    '.kill-c{'+
      'font-size: 1.2rem;'+
    '}'+
		'#l_header,#l_body{'+
			'display: none;'+
		'}'+
	'</style>'+
    '<div class="kill-ie">'+
        `<span class="kill-t"><b>Sorry, your browser cannot access this site</b></span><br/>`+
        `<span class="kill-c">Microsoft has terminated support for Internet Explorer (IE) 10 and earlier versions in 2016. <br/>There are great security risks to continue using it. Please use contemporary mainstream browsers to access.</span><br/>`+
        `<a target="_blank" rel="noopener" href="https://blogs.windows.com/windowsexperience/2021/05/19/the-future-of-internet-explorer-on-windows-10-is-in-microsoft-edge/"><strong>Learn more ></strong></a>`+
    '</div>');
</script>


<noscript>
	<style>
		html{
			overflow-x: hidden !important;
			overflow-y: hidden !important;
		}
		.kill-noscript{
			text-align:center;
			height: 100%;
			margin-top: 15%;
			margin-bottom: 5500%;
		}
    .kill-t{
      font-size: 2rem;
    }
    .kill-c{
      font-size: 1.2rem;
    }
		#l_header,#l_body{
			display: none;
		}
	</style>
    <div class="kill-noscript">
        <span class="kill-t"><b>Sorry, your browser cannot access this site</b></span><br/>
        <span class="kill-c">This page requires browser support (enable) JavaScript</span><br/>
        <a target="_blank" rel="noopener" href="https://www.baidu.com/s?wd=启用JavaScript"><strong>Learn more ></strong></a>
    </div>
</noscript>


  <script>
  /************这个文件存放不需要重载的全局变量和全局函数*********/
  window.volantis = {}; // volantis 全局变量
  volantis.debug = "env"; // 调试模式
  volantis.dom = {}; // 页面Dom see: /source/js/app.js etc.

  volantis.GLOBAL_CONFIG ={
    debug: "env",
    cdn: {"js":{"app":"/js/app.js","parallax":"/js/plugins/parallax.js","rightMenu":"/js/plugins/rightMenu.js","rightMenus":"/js/plugins/rightMenus.js","sites":"/js/plugins/tags/sites.js","friends":"/js/plugins/tags/friends.js","contributors":"/js/plugins/tags/contributors.js","search":"/js/search/hexo.js"},"css":{"style":"/css/style.css"}},
    default: {"avatar":"https://unpkg.com/volantis-static@0.0.1654736714924/media/placeholder/avatar/round/3442075.svg","link":"https://unpkg.com/volantis-static@0.0.1654736714924/media/placeholder/link/8f277b4ee0ecd.svg","cover":"https://unpkg.com/volantis-static@0.0.1654736714924/media/placeholder/cover/76b86c0226ffd.svg","image":"https://unpkg.com/volantis-static@0.0.1654736714924/media/placeholder/image/2659360.svg"},
    lastupdate: new Date(1708699590610),
    sidebar: {
      for_page: ["blogger","category","tagcloud","donate"],
      for_post: ["toc"],
      webinfo: {
        lastupd: {
          enable: true,
          friendlyShow: true
        },
        runtime: {
          data: "2020/01/01",
          unit: "天"
        }
      }
    },
    plugins: {
      message: {"enable":true,"css":"https://unpkg.com/volantis-static@0.0.1654736714924/libs/izitoast/dist/css/iziToast.min.css","js":"https://unpkg.com/volantis-static@0.0.1654736714924/libs/izitoast/dist/js/iziToast.min.js","icon":{"default":"fa-solid fa-info-circle light-blue","quection":"fa-solid fa-question-circle light-blue"},"time":{"default":5000,"quection":20000},"position":"topRight","transitionIn":"bounceInLeft","transitionOut":"fadeOutRight","titleColor":"var(--color-text)","messageColor":"var(--color-text)","backgroundColor":"var(--color-card)","zindex":2147483647,"copyright":{"enable":true,"title":"知识共享许可协议","message":"请遵守 CC BY-NC-SA 4.0 协议。","icon":"far fa-copyright light-blue"},"aplayer":{"enable":true,"play":"fa-solid fa-play","pause":"fa-solid fa-pause"},"rightmenu":{"enable":true,"notice":true}},
      fancybox: {"css":"https://unpkg.com/volantis-static@0.0.1654736714924/libs/@fancyapps/ui/dist/fancybox.css","js":"https://unpkg.com/volantis-static@0.0.1654736714924/libs/@fancyapps/ui/dist/fancybox.umd.js"},
      
      
      
    }
  }

  /******************** volantis.EventListener ********************************/
  // 事件监听器 see: /source/js/app.js
  volantis.EventListener = {}
  // 这里存放pjax切换页面时将被移除的事件监听器
  volantis.EventListener.list = []
  //构造方法
  function volantisEventListener(type, f, ele) {
    this.type = type
    this.f = f
    this.ele = ele
  }
  // 移除事件监听器
  volantis.EventListener.remove = () => {
    volantis.EventListener.list.forEach(function (i) {
      i.ele.removeEventListener(i.type, i.f, false)
    })
    volantis.EventListener.list = []
  }
  /******************** volantis.dom.$ ********************************/
  // 注：这里没有选择器，也没有forEach一次只处理一个dom，这里重新封装主题常用的dom方法，返回的是dom对象，对象包含了以下方法，同时保留dom的原生API
  function volantisDom(ele) {
    if (!ele) ele = document.createElement("div")
    this.ele = ele;
    // ==============================================================
    this.ele.find = (c) => {
      let q = this.ele.querySelector(c)
      if (q)
        return new volantisDom(q)
    }
    // ==============================================================
    this.ele.hasClass = (c) => {
      return this.ele.className.match(new RegExp('(\\s|^)' + c + '(\\s|$)'));
    }
    this.ele.addClass = (c) => {
      this.ele.classList.add(c);
      return this.ele
    }
    this.ele.removeClass = (c) => {
      this.ele.classList.remove(c);
      return this.ele
    }
    this.ele.toggleClass = (c) => {
      if (this.ele.hasClass(c)) {
        this.ele.removeClass(c)
      } else {
        this.ele.addClass(c)
      }
      return this.ele
    }
    // ==============================================================
    // 参数 r 为 true 表示pjax切换页面时事件监听器将被移除，false不移除
    this.ele.on = (c, f, r = 1) => {
      this.ele.addEventListener(c, f, false)
      if (r) {
        volantis.EventListener.list.push(new volantisEventListener(c, f, this.ele))
      }
      return this.ele
    }
    this.ele.click = (f, r) => {
      this.ele.on("click", f, r)
      return this.ele
    }
    this.ele.scroll = (f, r) => {
      this.ele.on("scroll", f, r)
      return this.ele
    }
    // ==============================================================
    this.ele.html = (c) => {
      // if(c=== undefined){
      //   return this.ele.innerHTML
      // }else{
      this.ele.innerHTML = c
      return this.ele
      // }
    }
    // ==============================================================
    this.ele.hide = (c) => {
      this.ele.style.display = "none"
      return this.ele
    }
    this.ele.show = (c) => {
      this.ele.style.display = "block"
      return this.ele
    }
    // ==============================================================
    return this.ele
  }
  volantis.dom.$ = (ele) => {
    return !!ele ? new volantisDom(ele) : null;
  }
  /******************** RunItem ********************************/
  function RunItem() {
    this.list = []; // 存放回调函数
    this.start = () => {
      for (var i = 0; i < this.list.length; i++) {
        this.list[i].run();
      }
    };
    this.push = (fn, name, setRequestAnimationFrame = true) => {
      let myfn = fn
      if (setRequestAnimationFrame) {
        myfn = ()=>{
          volantis.requestAnimationFrame(fn)
        }
      }
      var f = new Item(myfn, name);
      this.list.push(f);
    };
    this.remove = (name) =>{
      for (let index = 0; index < this.list.length; index++) {
        const e = this.list[index];
        if (e.name == name) {
          this.list.splice(index,1);
        }
      }
    }
    // 构造一个可以run的对象
    function Item(fn, name) {
      // 函数名称
      this.name = name || fn.name;
      // run方法
      this.run = () => {
        try {
          fn()
        } catch (error) {
          console.log(error);
        }
      };
    }
  }
  /******************** Pjax ********************************/
  // /layout/_plugins/pjax/index.ejs
  // volantis.pjax.send(callBack[,"callBackName"]) 传入pjax:send回调函数
  // volantis.pjax.push(callBack[,"callBackName"]) 传入pjax:complete回调函数
  // volantis.pjax.error(callBack[,"callBackName"]) 传入pjax:error回调函数
  volantis.pjax = {};
  volantis.pjax.method = {
    complete: new RunItem(),
    error: new RunItem(),
    send: new RunItem(),
  };
  volantis.pjax = Object.assign(volantis.pjax, {
    push: volantis.pjax.method.complete.push,
    error: volantis.pjax.method.error.push,
    send: volantis.pjax.method.send.push,
  });
  /******************** RightMenu ********************************/
  // volantis.rightmenu.handle(callBack[,"callBackName"]) 外部菜单项控制
  // 可在 volantis.mouseEvent 处获取右键事件
  volantis.rightmenu = {};
  volantis.rightmenu.method = {
    handle: new RunItem(),
  }
  volantis.rightmenu = Object.assign(volantis.rightmenu, {
    handle: volantis.rightmenu.method.handle.push,
  });
  /********************  Dark Mode  ********************************/
  // /layout/_partial/scripts/darkmode.ejs
  // volantis.dark.mode 当前模式 dark or light
  // volantis.dark.toggle() 暗黑模式触发器
  // volantis.dark.push(callBack[,"callBackName"]) 传入触发器回调函数
  volantis.dark = {};
  volantis.dark.method = {
    toggle: new RunItem(),
  };
  volantis.dark = Object.assign(volantis.dark, {
    push: volantis.dark.method.toggle.push,
  });
  /********************  Message  ********************************/
  // VolantisApp.message
  /********************  isMobile  ********************************/
  // /source/js/app.js
  // volantis.isMobile
  // volantis.isMobileOld
  /********************脚本动态加载函数********************************/
  // volantis.js(src, cb)  cb 可以传入onload回调函数 或者 JSON对象 例如: volantis.js("src", ()=>{}) 或 volantis.js("src", {defer:true,onload:()=>{}})
  // volantis.css(src)

  // 返回Promise对象，如下方法同步加载资源，这利于处理文件资源之间的依赖关系，例如：APlayer 需要在 MetingJS 之前加载
  // (async () => {
  //     await volantis.js("...theme.plugins.aplayer.js.aplayer...")
  //     await volantis.js("...theme.plugins.aplayer.js.meting...")
  // })();

  // 已经加入了setTimeout
  volantis.js = (src, cb) => {
    return new Promise(resolve => {
      setTimeout(function () {
        var HEAD = document.getElementsByTagName("head")[0] || document.documentElement;
        var script = document.createElement("script");
        script.setAttribute("type", "text/javascript");
        if (cb) {
          if (JSON.stringify(cb)) {
            for (let p in cb) {
              if (p == "onload") {
                script[p] = () => {
                  cb[p]()
                  resolve()
                }
              } else {
                script[p] = cb[p]
                script.onload = resolve
              }
            }
          } else {
            script.onload = () => {
              cb()
              resolve()
            };
          }
        } else {
          script.onload = resolve
        }
        script.setAttribute("src", src);
        HEAD.appendChild(script);
      });
    });
  }
  volantis.css = (src) => {
    return new Promise(resolve => {
      setTimeout(function () {
        var link = document.createElement('link');
        link.rel = "stylesheet";
        link.href = src;
        link.onload = resolve;
        document.getElementsByTagName("head")[0].appendChild(link);
      });
    });
  }
  /********************按需加载的插件********************************/
  // volantis.import.jQuery().then(()=>{})
  volantis.import = {
    jQuery: () => {
      if (typeof jQuery == "undefined") {
        return volantis.js("https://unpkg.com/volantis-static@0.0.1654736714924/libs/jquery/dist/jquery.min.js")
      } else {
        return new Promise(resolve => {
          resolve()
        });
      }
    }
  }
  /********************** requestAnimationFrame ********************************/
  // 1、requestAnimationFrame 会把每一帧中的所有 DOM 操作集中起来，在一次重绘或回流中就完成，并且重绘或回流的时间间隔紧紧跟随浏览器的刷新频率，一般来说，这个频率为每秒60帧。
  // 2、在隐藏或不可见的元素中，requestAnimationFrame 将不会进行重绘或回流，这当然就意味着更少的的 cpu，gpu 和内存使用量。
  volantis.requestAnimationFrame = (fn)=>{
    if (!window.requestAnimationFrame) {
      window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame;
    }
    window.requestAnimationFrame(fn)
  }
  /************************ layoutHelper *****************************************/
  volantis.layoutHelper = (helper, html, opt)=>{
    opt = Object.assign({clean:false, pjax:true}, opt)
    function myhelper(helper, html, clean) {
      volantis.tempDiv = document.createElement("div");
      volantis.tempDiv.innerHTML = html;
      let layoutHelper = document.querySelector("#layoutHelper-"+helper)
      if (layoutHelper) {
        if (clean) {
          layoutHelper.innerHTML = ""
        }
        layoutHelper.append(volantis.tempDiv);
      }
    }
    myhelper(helper, html, opt.clean)
    if (opt.pjax) {
      volantis.pjax.push(()=>{
        myhelper(helper, html, opt.clean)
      },"layoutHelper-"+helper)
    }
  }
  /****************************** 滚动事件处理 ****************************************/
  volantis.scroll = {
    engine: new RunItem(),
    unengine: new RunItem(),
  };
  volantis.scroll = Object.assign(volantis.scroll, {
    push: volantis.scroll.engine.push,
  });
  // 滚动条距离顶部的距离
  volantis.scroll.getScrollTop = () =>{
    let scrollPos;
    if (window.pageYOffset) {
      scrollPos = window.pageYOffset;
    } else if (document.compatMode && document.compatMode != 'BackCompat') {
      scrollPos = document.documentElement.scrollTop;
    } else if (document.body) {
      scrollPos = document.body.scrollTop;
    }
    return scrollPos;
  }
  // 使用 requestAnimationFrame 处理滚动事件
  // `volantis.scroll.del` 中存储了一个数值, 该数值检测一定时间间隔内滚动条滚动的位移, 数值的检测频率是浏览器的刷新频率. 数值为正数时, 表示向下滚动. 数值为负数时, 表示向上滚动.
  volantis.scroll.handleScrollEvents = () => {
    volantis.scroll.lastScrollTop = volantis.scroll.getScrollTop()
    function loop() {
      const scrollTop = volantis.scroll.getScrollTop();
      if (volantis.scroll.lastScrollTop !== scrollTop) {
        volantis.scroll.del = scrollTop - volantis.scroll.lastScrollTop;
        volantis.scroll.lastScrollTop = scrollTop;
        // if (volantis.scroll.del > 0) {
        //   console.log("向下滚动");
        // } else {
        //   console.log("向上滚动");
        // }
        // 注销过期的unengine未滚动事件
        volantis.scroll.unengine.list=[]
        volantis.scroll.engine.start();
      }else{
        volantis.scroll.unengine.start();
      }
      volantis.requestAnimationFrame(loop)
    }
    volantis.requestAnimationFrame(loop)
  }
  volantis.scroll.handleScrollEvents()
  volantis.scroll.ele = null;
  // 触发页面滚动至目标元素位置
  volantis.scroll.to = (ele, option = {}) => {
    if (!ele) return;
    volantis.scroll.ele = ele;
    // 默认配置
    opt = {
      top: ele.getBoundingClientRect().top + document.documentElement.scrollTop,
      behavior: "smooth"
    }
    // 定义配置
    if ("top" in option) {
      opt.top = option.top
    }
    if ("behavior" in option) {
      opt.behavior = option.behavior
    }
    if ("addTop" in option) {
      opt.top += option.addTop
    }
    if (!("observerDic" in option)) {
      option.observerDic = 100
    }
    // 滚动
    window.scrollTo(opt);
    // 监视器
    // 监视并矫正元素滚动到指定位置
    // 用于处理 lazyload 引起的 cls 导致的定位失败问题
    // option.observer = false
    if (option.observer) {
      setTimeout(() => {
        if (volantis.scroll.ele != ele) {
          return
        }
        volantis.scroll.unengine.push(() => {
          let me = ele.getBoundingClientRect().top
          if(!(me >= -option.observerDic && me <= option.observerDic)){
            volantis.scroll.to(ele, option)
          }
          volantis.scroll.unengine.remove("unengineObserver")
        },"unengineObserver")
      },1000)
    }
  }
  /********************** Content Visibility ********************************/
  // 见 source/css/first.styl 如果遇到任何问题 删除 .post-story 即可
  // 一个元素被声明 content-visibility 属性后 如果元素不在 viewport 中 浏览器不会计算其后代元素样式和属性 从而节省 Style & Layout 耗时
  // content-visibility 的副作用: 锚点失效 等等(实验初期 暂不明确), 使用此方法清除样式
  volantis.cleanContentVisibility = ()=>{
    if (document.querySelector(".post-story")) {
      console.log("cleanContentVisibility");
      document.querySelectorAll(".post-story").forEach(e=>{
        e.classList.remove("post-story")
      })
    }
  }
  /******************************************************************************/
  /******************************************************************************/
  /******************************************************************************/
  //图像加载出错时的处理
  function errorImgAvatar(img) {
    img.src = "https://unpkg.com/volantis-static@0.0.1654736714924/media/placeholder/avatar/round/3442075.svg";
    img.onerror = null;
  }
  function errorImgCover(img) {
    img.src = "https://unpkg.com/volantis-static@0.0.1654736714924/media/placeholder/cover/76b86c0226ffd.svg";
    img.onerror = null;
  }
  /******************************************************************************/
</script>

  <!-- import head_end begin -->
  <!-- import head_end end -->
  <!-- Custom Files headEnd begin-->
  
  <!-- Custom Files headEnd end-->
  <!-- front-matter head_end begin -->
  <!-- front-matter head_end end -->
</head>
  <body itemscope itemtype="http://schema.org/WebPage">
    <!-- import body_begin begin-->
    <!-- import body_begin end-->
    <!-- Custom Files bodyBegin begin-->
    
    <!-- Custom Files bodyBegin end-->
    <!-- front-matter body_begin begin -->
    <!-- front-matter body_begin end -->
    <header itemscope itemtype="http://schema.org/WPHeader" id="l_header" class="l_header auto shadow floatable blur show" style='opacity: 0' >
  <div class='container'>
  <div id='wrapper'>
    <div class='nav-sub'>
      <p class="title"></p>
      <ul class='switcher nav-list-h m-phone' id="pjax-header-nav-list">
        <li><a id="s-comment" class="fa-solid fa-comments fa-fw" target="_self"  href="/" onclick="return false;" title="comment"></a></li>
        
          <li><a id="s-toc" class="s-toc fa-solid fa-list fa-fw" target="_self"  href="/" onclick="return false;" title="toc"></a></li>
        
      </ul>
    </div>
		<div class="nav-main">
      
        
        <a class="title flat-box" target="_self" href='/'>
          
            <img no-lazy class='logo' src='https://unpkg.com/volantis-static@0.0.1654736714924/media/org.volantis/blog/Logo-NavBar@3x.png'/>
          
          
          
        </a>
      

			<div class='menu navigation'>
				<ul class='nav-list-h m-pc'>
          
          
          
            
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover"
                href="/" title="博客"
                  
                  
                  
                    active-action="action-home"
                  >
                  <i class='fa-solid fa-rss fa-fw'></i>博客
                </a>
                
              </li>
            
          
          
            
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover"
                href="/categories/" title="分类"
                  
                  
                  
                    active-action="action-categories"
                  >
                  <i class='fa-solid fa-folder-open fa-fw'></i>分类
                </a>
                
              </li>
            
          
          
            
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover"
                href="/tags/" title="标签"
                  
                  
                  
                    active-action="action-tags"
                  >
                  <i class='fa-solid fa-tags fa-fw'></i>标签
                </a>
                
              </li>
            
          
          
            
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover"
                href="/archives/" title="归档"
                  
                  
                  
                    active-action="action-archives"
                  >
                  <i class='fa-solid fa-archive fa-fw'></i>归档
                </a>
                
              </li>
            
          
          
            
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover"
                href="/friends/" title="友链"
                  
                  
                  
                    active-action="action-friends"
                  >
                  <i class='fa-solid fa-link fa-fw'></i>友链
                </a>
                
              </li>
            
          
          
            
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover"
                href="/about/" title="关于"
                  
                  
                  
                    active-action="action-about"
                  >
                  <i class='fa-solid fa-info-circle fa-fw'></i>关于
                </a>
                
              </li>
            
          
          
				</ul>
			</div>
      
      <div class="m_search">
        <form name="searchform" class="form u-search-form">
          <i class="icon fa-solid fa-search fa-fw"></i>
          <input type="text" class="input u-search-input" placeholder="Search..." />
        </form>
      </div>
      

			<ul class='switcher nav-list-h m-phone'>
				
					<li><a class="s-search fa-solid fa-search fa-fw" target="_self" href="/" onclick="return false;" title="search"></a></li>
				
				<li>
          <a class="s-menu fa-solid fa-bars fa-fw" target="_self" href="/" onclick="return false;" title="menu"></a>
          <ul class="menu-phone list-v navigation white-box">
            
              
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover"
                href="/" title="博客"
                  
                  
                  
                    active-action="action-home"
                  >
                  <i class='fa-solid fa-rss fa-fw'></i>博客
                </a>
                
              </li>
            
          
            
              
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover"
                href="/categories/" title="分类"
                  
                  
                  
                    active-action="action-categories"
                  >
                  <i class='fa-solid fa-folder-open fa-fw'></i>分类
                </a>
                
              </li>
            
          
            
              
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover"
                href="/tags/" title="标签"
                  
                  
                  
                    active-action="action-tags"
                  >
                  <i class='fa-solid fa-tags fa-fw'></i>标签
                </a>
                
              </li>
            
          
            
              
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover"
                href="/archives/" title="归档"
                  
                  
                  
                    active-action="action-archives"
                  >
                  <i class='fa-solid fa-archive fa-fw'></i>归档
                </a>
                
              </li>
            
          
            
              
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover"
                href="/friends/" title="友链"
                  
                  
                  
                    active-action="action-friends"
                  >
                  <i class='fa-solid fa-link fa-fw'></i>友链
                </a>
                
              </li>
            
          
            
              
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover"
                href="/about/" title="关于"
                  
                  
                  
                    active-action="action-about"
                  >
                  <i class='fa-solid fa-info-circle fa-fw'></i>关于
                </a>
                
              </li>
            
          
            
          </ul>
        </li>
			</ul>

      <!-- Custom Files header begin -->
      
      <!-- Custom Files header end -->
		</div>
	</div>
  </div>
</header>

    <div id="l_body">
      <div id="l_cover">
  
    
      <!-- see: /layout/_partial/scripts/_ctrl/coverCtrl.ejs -->
      <div id="none" class='cover-wrapper post dock' style="display: none;">
        
  <div class='cover-bg lazyload placeholder' data-bg="https://gcore.jsdelivr.net/gh/MHG-LAB/cron@gh-pages/bing/bing.jpg"></div>

<div class='cover-body'>
  <div class='top'>
    
    
      <p class="title">Volantis</p>
    
    
  </div>
  <div class='bottom'>
    <div class='menu navigation'>
      <div class='list-h'>
        
          
            <a href="/v4/getting-started/"
              
              
              active-action="action-v4getting-started">
              <img src='https://unpkg.com/volantis-static@0.0.1654736714924/media/twemoji/assets/svg/1f5c3.svg'><p>文档</p>
            </a>
          
            <a href="/faqs/"
              
              
              active-action="action-faqs">
              <img src='https://unpkg.com/volantis-static@0.0.1654736714924/media/twemoji/assets/svg/1f516.svg'><p>帮助</p>
            </a>
          
            <a href="/examples/"
              
              
              active-action="action-examples">
              <img src='https://unpkg.com/volantis-static@0.0.1654736714924/media/twemoji/assets/svg/1f396.svg'><p>示例</p>
            </a>
          
            <a href="/contributors/"
              
              
              active-action="action-contributors">
              <img src='https://unpkg.com/volantis-static@0.0.1654736714924/media/twemoji/assets/svg/1f389.svg'><p>社区</p>
            </a>
          
            <a href="/archives/"
              
              
              active-action="action-archives">
              <img src='https://unpkg.com/volantis-static@0.0.1654736714924/media/twemoji/assets/svg/1f4f0.svg'><p>博客</p>
            </a>
          
            <a target="_blank" rel="noopener" href="https://github.com/volantis-x/hexo-theme-volantis/"
              
              
              active-action="action-https:githubcomvolantis-xhexo-theme-volantis">
              <img src='https://unpkg.com/volantis-static@0.0.1654736714924/media/twemoji/assets/svg/1f9ec.svg'><p>源码</p>
            </a>
          
        
      </div>
    </div>
  </div>
</div>

        <div id="scroll-down" style="display: none;"><i class="fa fa-chevron-down scroll-down-effects"></i></div>
      </div>
    
  
</div>

      <div id="safearea">
        <div class="body-wrapper">
          
<div id="l_main" class=''>
  <article itemscope itemtype="http://schema.org/Article" class="article post white-box reveal md shadow floatable blur article-type-post" id="post" itemscope itemprop="blogPost">
  <link itemprop="mainEntityOfPage" href="http://8.219.11.28/2024/02/23/工具-Redis/">
  <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
    <meta itemprop="name" content="欢迎来到我的个人博客~">
  </span>
  <span hidden itemprop="post" itemscope itemtype="http://schema.org/Post">
    <meta itemprop="name" content="欢迎来到我的个人博客~">
    <meta itemprop="description" content="这些技术大部分是自己总结的，非官方">
  </span>
  


  
    <span hidden>
      <meta itemprop="image" content="https://unpkg.com/volantis-static@0.0.1654736714924/media/org.volantis/blog/favicon/android-chrome-192x192.png">
    </span>
  
  <div class="article-meta" id="top">
    
    
    
      <span hidden itemprop="name headline">
        
      </span>
    
  </div>


  <div id="layoutHelper-page-plugins"></div>
  <div id="post-body" itemprop="articleBody">
    <h1 id="基础篇Redis"><a href="#基础篇Redis" class="headerlink" title="基础篇Redis"></a>基础篇Redis</h1><h2 id="1-Redis简单介绍"><a href="#1-Redis简单介绍" class="headerlink" title="1.Redis简单介绍"></a>1.Redis简单介绍</h2><p>Redis是一种键值型的NoSql数据库，这里有两个关键字：</p>
<ul>
<li>键值型</li>
<li>NoSql</li>
</ul>
<p>其中<strong>键值型</strong>，是指Redis中存储的数据都是以key.value对的形式存储，而value的形式多种多样，可以是字符串.数值.甚至json：</p>
<p><img src="F:\JAVA笔记库\img\101010.png" class="lazyload" data-srcset="F:\JAVA笔记库\img\101010.png" srcset=""></p>
<p>而NoSql则是相对于传统关系型数据库而言，有很大差异的一种数据库。</p>
<p>对于存储的数据，没有类似Mysql那么严格的约束，比如唯一性，是否可以为null等等，所以我们把这种松散结构的数据库，称之为NoSQL数据库。</p>
<h2 id="2-课程目录"><a href="#2-课程目录" class="headerlink" title="2.课程目录"></a>2.课程目录</h2><blockquote>
<ul>
<li>初始Redis<ul>
<li>认识NoSQL</li>
<li>认识Redis</li>
<li>安装Redis</li>
</ul>
</li>
<li>Redis常见命令<ul>
<li>5种常见数据结构</li>
<li>通用命令</li>
<li>不同数据结构的操作命令</li>
</ul>
</li>
<li>Redis的Java客户端<ul>
<li>Jedis客户端</li>
<li>SpringDataRedis客户端</li>
</ul>
</li>
</ul>
</blockquote>
<h2 id="3-初始Redis"><a href="#3-初始Redis" class="headerlink" title="3.初始Redis"></a>3.初始Redis</h2><h3 id="3-1-认识NoSQL"><a href="#3-1-认识NoSQL" class="headerlink" title="3.1.认识NoSQL"></a>3.1.认识NoSQL</h3><p><strong>NoSql</strong>可以翻译做Not Only Sql（不仅仅是SQL），或者是No Sql（非Sql的）数据库。是相对于传统关系型数据库而言，有很大差异的一种特殊的数据库，因此也称之为<strong>非关系型数据库</strong>。</p>
<h4 id="3-1-1-结构化与非结构化"><a href="#3-1-1-结构化与非结构化" class="headerlink" title="3.1.1.结构化与非结构化"></a>3.1.1.结构化与非结构化</h4><p>传统关系型数据库是结构化数据，每一张表都有严格的约束信息：字段名.字段数据类型.字段约束等等信息，插入的数据必须遵守这些约束：</p>
<p><img src="https://i.imgur.com/4tUgFo6.png" class="lazyload" data-srcset="https://i.imgur.com/4tUgFo6.png" srcset=""></p>
<p>而NoSql则对数据库格式没有严格约束，往往形式松散，自由。</p>
<p>可以是键值型：</p>
<p><img src="https://i.imgur.com/GdqOSsj.png" class="lazyload" data-srcset="https://i.imgur.com/GdqOSsj.png" srcset=""></p>
<p>也可以是文档型：</p>
<p><img src="https://i.imgur.com/zBBQfcc.png" class="lazyload" data-srcset="https://i.imgur.com/zBBQfcc.png" srcset=""></p>
<p>甚至可以是图格式：</p>
<p><img src="https://i.imgur.com/zBnKxWf.png" class="lazyload" data-srcset="https://i.imgur.com/zBnKxWf.png" srcset=""></p>
<h4 id="3-1-2-关联和非关联"><a href="#3-1-2-关联和非关联" class="headerlink" title="3.1.2.关联和非关联"></a>3.1.2.关联和非关联</h4><p>传统数据库的表与表之间往往存在关联，例如外键：</p>
<p><img src="https://i.imgur.com/tXYSl5x.png" class="lazyload" data-srcset="https://i.imgur.com/tXYSl5x.png" srcset=""></p>
<p>而非关系型数据库不存在关联关系，要维护关系要么靠代码中的业务逻辑，要么靠数据之间的耦合：</p>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">  id<span class="punctuation">:</span> <span class="number">1</span><span class="punctuation">,</span></span><br><span class="line">  name<span class="punctuation">:</span> <span class="string">&quot;张三&quot;</span><span class="punctuation">,</span></span><br><span class="line">  orders<span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line">    <span class="punctuation">&#123;</span></span><br><span class="line">       id<span class="punctuation">:</span> <span class="number">1</span><span class="punctuation">,</span></span><br><span class="line">       item<span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">	 id<span class="punctuation">:</span> <span class="number">10</span><span class="punctuation">,</span> title<span class="punctuation">:</span> <span class="string">&quot;荣耀6&quot;</span><span class="punctuation">,</span> price<span class="punctuation">:</span> <span class="number">4999</span></span><br><span class="line">       <span class="punctuation">&#125;</span></span><br><span class="line">    <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="punctuation">&#123;</span></span><br><span class="line">       id<span class="punctuation">:</span> <span class="number">2</span><span class="punctuation">,</span></span><br><span class="line">       item<span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">	 id<span class="punctuation">:</span> <span class="number">20</span><span class="punctuation">,</span> title<span class="punctuation">:</span> <span class="string">&quot;小米11&quot;</span><span class="punctuation">,</span> price<span class="punctuation">:</span> <span class="number">3999</span></span><br><span class="line">       <span class="punctuation">&#125;</span></span><br><span class="line">    <span class="punctuation">&#125;</span></span><br><span class="line">  <span class="punctuation">]</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure>

<p>此处要维护“张三”的订单与商品“荣耀”和“小米11”的关系，不得不冗余的将这两个商品保存在张三的订单文档中，不够优雅。还是建议用业务来维护关联关系。</p>
<h4 id="3-1-3-查询方式"><a href="#3-1-3-查询方式" class="headerlink" title="3.1.3.查询方式"></a>3.1.3.查询方式</h4><p>传统关系型数据库会基于Sql语句做查询，语法有统一标准；</p>
<p>而不同的非关系数据库查询语法差异极大，五花八门各种各样。</p>
<p><img src="https://i.imgur.com/AzaHOTF.png" class="lazyload" data-srcset="https://i.imgur.com/AzaHOTF.png" srcset=""></p>
<h4 id="3-1-4-事务"><a href="#3-1-4-事务" class="headerlink" title="3.1.4.事务"></a>3.1.4.事务</h4><p>传统关系型数据库能满足事务ACID的原则。</p>
<p><img src="https://i.imgur.com/J1MqOJM.png" class="lazyload" data-srcset="https://i.imgur.com/J1MqOJM.png" srcset=""></p>
<p>而非关系型数据库往往不支持事务，或者不能严格保证ACID的特性，只能实现基本的一致性。</p>
<h4 id="3-1-5-总结"><a href="#3-1-5-总结" class="headerlink" title="3.1.5.总结"></a>3.1.5.总结</h4><p>除了上述四点以外，在存储方式.扩展性.查询性能上关系型与非关系型也都有着显著差异，总结如下：</p>
<p><img src="https://i.imgur.com/kZP40dQ.png" class="lazyload" data-srcset="https://i.imgur.com/kZP40dQ.png" srcset=""></p>
<ul>
<li>存储方式<ul>
<li>关系型数据库基于磁盘进行存储，会有大量的磁盘IO，对性能有一定影响</li>
<li>非关系型数据库，他们的操作更多的是依赖于内存来操作，内存的读写速度会非常快，性能自然会好一些</li>
</ul>
</li>
</ul>
<ul>
<li>扩展性<ul>
<li>关系型数据库集群模式一般是主从，主从数据一致，起到数据备份的作用，称为垂直扩展。</li>
<li>非关系型数据库可以将数据拆分，存储在不同机器上，可以保存海量数据，解决内存大小有限的问题。称为水平扩展。</li>
<li>关系型数据库因为表之间存在关联关系，如果做水平扩展会给数据查询带来很多麻烦</li>
</ul>
</li>
</ul>
<h3 id="3-2-认识Redis"><a href="#3-2-认识Redis" class="headerlink" title="3.2.认识Redis"></a>3.2.认识Redis</h3><p>Redis诞生于2009年全称是<strong>Re</strong>mote  <strong>D</strong>ictionary <strong>S</strong>erver 远程词典服务器，是一个基于内存的键值型NoSQL数据库。</p>
<p><strong>特征</strong>：</p>
<ul>
<li>键值（key-value）型，value支持多种不同数据结构，功能丰富</li>
<li>单线程，每个命令具备原子性</li>
<li>低延迟，速度快（基于内存.IO多路复用.良好的编码）。</li>
<li>支持数据持久化</li>
<li>支持主从集群.分片集群</li>
<li>支持多语言客户端</li>
</ul>
<p><strong>作者</strong>：Antirez</p>
<p>Redis的官方网站地址：<a target="_blank" rel="noopener" href="https://redis.io/">https://redis.io/</a></p>
<h3 id="3-3-安装Redis"><a href="#3-3-安装Redis" class="headerlink" title="3.3.安装Redis"></a>3.3.安装Redis</h3><p>大多数企业都是基于Linux服务器来部署项目，而且Redis官方也没有提供Windows版本的安装包。因此课程中我们会基于Linux系统来安装Redis.</p>
<p>此处选择的Linux版本为CentOS 7.</p>
<h4 id="3-3-1-依赖库"><a href="#3-3-1-依赖库" class="headerlink" title="3.3.1.依赖库"></a>3.3.1.依赖库</h4><p>Redis是基于C语言编写的，因此首先需要安装Redis所需要的gcc依赖：</p>
<figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">yum install -y gcc tcl</span><br></pre></td></tr></table></figure>



<h4 id="3-3-2-上传安装包并解压"><a href="#3-3-2-上传安装包并解压" class="headerlink" title="3.3.2.上传安装包并解压"></a>3.3.2.上传安装包并解压</h4><p>然后将课前资料提供的Redis安装包上传到虚拟机的任意目录：</p>
<p><img src="https://i.imgur.com/SyjanS5.png" class="lazyload" data-srcset="https://i.imgur.com/SyjanS5.png" srcset=""></p>
<p>例如，我放到了&#x2F;usr&#x2F;local&#x2F;src 目录：</p>
<p><img src="https://i.imgur.com/01DTNCf.png" class="lazyload" data-srcset="https://i.imgur.com/01DTNCf.png" srcset=""></p>
<p>解压缩：</p>
<figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">tar -xzf redis-6.2.6.tar.gz</span><br></pre></td></tr></table></figure>

<p>解压后：</p>
<p><img src="https://i.imgur.com/8V6zvCD.png" class="lazyload" data-srcset="https://i.imgur.com/8V6zvCD.png" srcset="" alt="image-20211211080339076"></p>
<p>进入redis目录：</p>
<figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">cd</span> redis-6.2.6</span><br></pre></td></tr></table></figure>



<p>运行编译命令：</p>
<figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">make &amp;&amp; make install</span><br></pre></td></tr></table></figure>

<p>如果没有出错，应该就安装成功了。</p>
<p>默认的安装路径是在 <code>/usr/local/bin</code>目录下：</p>
<p><img src="https://i.imgur.com/YSxkGm7.png" class="lazyload" data-srcset="https://i.imgur.com/YSxkGm7.png" srcset=""></p>
<p>该目录已经默认配置到环境变量，因此可以在任意目录下运行这些命令。其中：</p>
<ul>
<li>redis-cli：是redis提供的命令行客户端</li>
<li>redis-server：是redis的服务端启动脚本</li>
<li>redis-sentinel：是redis的哨兵启动脚本</li>
</ul>
<h4 id="3-3-3-启动"><a href="#3-3-3-启动" class="headerlink" title="3.3.3.启动"></a>3.3.3.启动</h4><p>redis的启动方式有很多种，例如：</p>
<ul>
<li>默认启动</li>
<li>指定配置启动</li>
<li>开机自启</li>
</ul>
<h4 id="3-3-4-默认启动"><a href="#3-3-4-默认启动" class="headerlink" title="3.3.4.默认启动"></a>3.3.4.默认启动</h4><p>安装完成后，在任意目录输入redis-server命令即可启动Redis：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">redis-server</span><br></pre></td></tr></table></figure>

<p>如图：</p>
<p><img src="https://i.imgur.com/v7xWsqC.png" class="lazyload" data-srcset="https://i.imgur.com/v7xWsqC.png" srcset=""></p>
<p>这种启动属于<code>前台启动</code>，会阻塞整个会话窗口，窗口关闭或者按下<code>CTRL + C</code>则Redis停止。不推荐使用。</p>
<h4 id="3-3-5-指定配置启动"><a href="#3-3-5-指定配置启动" class="headerlink" title="3.3.5.指定配置启动"></a>3.3.5.指定配置启动</h4><p>如果要让Redis以<code>后台</code>方式启动，则必须修改Redis配置文件，就在我们之前解压的redis安装包下（<code>/usr/local/src/redis-6.2.6</code>），名字叫redis.conf：</p>
<p><img src="/assets/image-20211211082225509.png" class="lazyload" data-srcset="/assets/image-20211211082225509.png" srcset="" alt="image-20211211082225509"></p>
<p>我们先将这个配置文件备份一份：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cp redis.conf redis.conf.bck</span><br></pre></td></tr></table></figure>



<p>然后修改redis.conf文件中的一些配置：</p>
<figure class="highlight properties"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 允许访问的地址，默认是127.0.0.1，会导致只能在本地访问。修改为0.0.0.0则可以在任意IP访问，生产环境不要设置为0.0.0.0</span></span><br><span class="line"><span class="attr">bind</span> <span class="string">0.0.0.0</span></span><br><span class="line"><span class="comment"># 守护进程，修改为yes后即可后台运行</span></span><br><span class="line"><span class="attr">daemonize</span> <span class="string">yes </span></span><br><span class="line"><span class="comment"># 密码，设置后访问Redis必须输入密码</span></span><br><span class="line"><span class="attr">requirepass</span> <span class="string">123321</span></span><br></pre></td></tr></table></figure>



<p>Redis的其它常见配置：</p>
<figure class="highlight properties"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 监听的端口</span></span><br><span class="line"><span class="attr">port</span> <span class="string">6379</span></span><br><span class="line"><span class="comment"># 工作目录，默认是当前目录，也就是运行redis-server时的命令，日志.持久化等文件会保存在这个目录</span></span><br><span class="line"><span class="attr">dir</span> <span class="string">.</span></span><br><span class="line"><span class="comment"># 数据库数量，设置为1，代表只使用1个库，默认有16个库，编号0~15</span></span><br><span class="line"><span class="attr">databases</span> <span class="string">1</span></span><br><span class="line"><span class="comment"># 设置redis能够使用的最大内存</span></span><br><span class="line"><span class="attr">maxmemory</span> <span class="string">512mb</span></span><br><span class="line"><span class="comment"># 日志文件，默认为空，不记录日志，可以指定日志文件名</span></span><br><span class="line"><span class="attr">logfile</span> <span class="string">&quot;redis.log&quot;</span></span><br></pre></td></tr></table></figure>



<p>启动Redis：</p>
<figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 进入redis安装目录 </span></span><br><span class="line"><span class="built_in">cd</span> /usr/local/src/redis-6.2.6</span><br><span class="line"><span class="comment"># 启动</span></span><br><span class="line">redis-server redis.conf</span><br></pre></td></tr></table></figure>



<p>停止服务：</p>
<figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 利用redis-cli来执行 shutdown 命令，即可停止 Redis 服务，</span></span><br><span class="line"><span class="comment"># 因为之前配置了密码，因此需要通过 -u 来指定密码</span></span><br><span class="line">redis-cli -u 123321 shutdown</span><br></pre></td></tr></table></figure>



<h4 id="3-3-6-开机自启"><a href="#3-3-6-开机自启" class="headerlink" title="3.3.6.开机自启"></a>3.3.6.开机自启</h4><p>我们也可以通过配置来实现开机自启。</p>
<p>首先，新建一个系统服务文件：</p>
<figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">vi /etc/systemd/system/redis.service</span><br></pre></td></tr></table></figure>

<p>内容如下：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">[Unit]</span><br><span class="line">Description=redis-server</span><br><span class="line">After=network.target</span><br><span class="line"></span><br><span class="line">[Service]</span><br><span class="line">Type=forking</span><br><span class="line">ExecStart=/usr/local/bin/redis-server /usr/local/src/redis-6.2.6/redis.conf</span><br><span class="line">PrivateTmp=true</span><br><span class="line"></span><br><span class="line">[Install]</span><br><span class="line">WantedBy=multi-user.target</span><br></pre></td></tr></table></figure>



<p>然后重载系统服务：</p>
<figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">systemctl daemon-reload</span><br></pre></td></tr></table></figure>



<p>现在，我们可以用下面这组命令来操作redis了：</p>
<figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 启动</span></span><br><span class="line">systemctl start redis</span><br><span class="line"><span class="comment"># 停止</span></span><br><span class="line">systemctl stop redis</span><br><span class="line"><span class="comment"># 重启</span></span><br><span class="line">systemctl restart redis</span><br><span class="line"><span class="comment"># 查看状态</span></span><br><span class="line">systemctl status redis</span><br></pre></td></tr></table></figure>



<p>执行下面的命令，可以让redis开机自启：</p>
<figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">systemctl <span class="built_in">enable</span> redis</span><br></pre></td></tr></table></figure>



<h3 id="3-4-Redis桌面客户端"><a href="#3-4-Redis桌面客户端" class="headerlink" title="3.4.Redis桌面客户端"></a>3.4.Redis桌面客户端</h3><p>安装完成Redis，我们就可以操作Redis，实现数据的CRUD了。这需要用到Redis客户端，包括：</p>
<ul>
<li>命令行客户端</li>
<li>图形化桌面客户端</li>
<li>编程客户端</li>
</ul>
<h4 id="3-4-1-Redis命令行客户端"><a href="#3-4-1-Redis命令行客户端" class="headerlink" title="3.4.1.Redis命令行客户端"></a>3.4.1.Redis命令行客户端</h4><p>Redis安装完成后就自带了命令行客户端：redis-cli，使用方式如下：</p>
<figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">redis-cli [options] [commonds]</span><br></pre></td></tr></table></figure>

<p>其中常见的options有：</p>
<ul>
<li><code>-h 127.0.0.1</code>：指定要连接的redis节点的IP地址，默认是127.0.0.1</li>
<li><code>-p 6379</code>：指定要连接的redis节点的端口，默认是6379</li>
<li><code>-a 123321</code>：指定redis的访问密码</li>
</ul>
<p>其中的commonds就是Redis的操作命令，例如：</p>
<ul>
<li><code>ping</code>：与redis服务端做心跳测试，服务端正常会返回<code>pong</code></li>
</ul>
<p>不指定commond时，会进入<code>redis-cli</code>的交互控制台：</p>
<p><img src="https://i.imgur.com/OYYWPNo.png" class="lazyload" data-srcset="https://i.imgur.com/OYYWPNo.png" srcset=""></p>
<h4 id="3-4-2-图形化桌面客户端"><a href="#3-4-2-图形化桌面客户端" class="headerlink" title="3.4.2.图形化桌面客户端"></a>3.4.2.图形化桌面客户端</h4><p>GitHub上的大神编写了Redis的图形化桌面客户端，地址：<a target="_blank" rel="noopener" href="https://github.com/uglide/RedisDesktopManager">https://github.com/uglide/RedisDesktopManager</a></p>
<p>不过该仓库提供的是RedisDesktopManager的源码，并未提供windows安装包。</p>
<p>在下面这个仓库可以找到安装包：<a target="_blank" rel="noopener" href="https://github.com/lework/RedisDesktopManager-Windows/releases">https://github.com/lework/RedisDesktopManager-Windows/releases</a></p>
<h4 id="3-4-3-安装"><a href="#3-4-3-安装" class="headerlink" title="3.4.3.安装"></a>3.4.3.安装</h4><p>在课前资料中可以找到Redis的图形化桌面客户端：</p>
<p><img src="https://i.imgur.com/BZ4Agbi.png" class="lazyload" data-srcset="https://i.imgur.com/BZ4Agbi.png" srcset=""></p>
<p>解压缩后，运行安装程序即可安装：</p>
<p><img src="https://i.imgur.com/hguGHbX.png" class="lazyload" data-srcset="https://i.imgur.com/hguGHbX.png" srcset=""></p>
<p>安装完成后，在安装目录下找到rdm.exe文件：</p>
<p><img src="https://i.imgur.com/hwK5LQ8.png" class="lazyload" data-srcset="https://i.imgur.com/hwK5LQ8.png" srcset=""></p>
<p>双击即可运行：</p>
<p><img src="https://i.imgur.com/6hUqslY.png" class="lazyload" data-srcset="https://i.imgur.com/6hUqslY.png" srcset=""></p>
<h4 id="3-4-4-建立连接"><a href="#3-4-4-建立连接" class="headerlink" title="3.4.4.建立连接"></a>3.4.4.建立连接</h4><p>点击左上角的<code>连接到Redis服务器</code>按钮：</p>
<p><img src="https://i.imgur.com/9qTGyoN.png" class="lazyload" data-srcset="https://i.imgur.com/9qTGyoN.png" srcset=""></p>
<p>在弹出的窗口中填写Redis服务信息：</p>
<p><img src="https://i.imgur.com/DshNnKC.png" class="lazyload" data-srcset="https://i.imgur.com/DshNnKC.png" srcset=""></p>
<p>点击确定后，在左侧菜单会出现这个链接：</p>
<p><img src="https://i.imgur.com/A2cOm7Q.png" class="lazyload" data-srcset="https://i.imgur.com/A2cOm7Q.png" srcset=""></p>
<p>点击即可建立连接了。</p>
<p><img src="https://i.imgur.com/ja8Fd9s.png" class="lazyload" data-srcset="https://i.imgur.com/ja8Fd9s.png" srcset=""></p>
<p>Redis默认有16个仓库，编号从0至15.  通过配置文件可以设置仓库数量，但是不超过16，并且不能自定义仓库名称。</p>
<p>如果是基于redis-cli连接Redis服务，可以通过select命令来选择数据库：</p>
<figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 选择 0号库</span></span><br><span class="line"><span class="keyword">select</span> 0</span><br></pre></td></tr></table></figure>



<h2 id="4-Redis常见命令"><a href="#4-Redis常见命令" class="headerlink" title="4.Redis常见命令"></a>4.Redis常见命令</h2><h3 id="4-1-Redis数据结构介绍"><a href="#4-1-Redis数据结构介绍" class="headerlink" title="4.1 Redis数据结构介绍"></a>4.1 Redis数据结构介绍</h3><p>Redis是一个key-value的数据库，key一般是String类型，不过value的类型多种多样： </p>
<p><img src="/assets/1652887393157.png" class="lazyload" data-srcset="/assets/1652887393157.png" srcset="" alt="1652887393157"></p>
<p><strong>String - String  String - Hash  String-List String -Set  String-SortedSet  String-GEO…..</strong></p>
<p><img src="F:\JAVA笔记库\img\10101010.png" class="lazyload" data-srcset="F:\JAVA笔记库\img\10101010.png" srcset=""></p>
<p><strong>贴心小建议：命令不要死记，学会查询就好啦</strong></p>
<p>Redis为了方便我们学习，将操作不同数据类型的命令也做了分组，在官网（ <a target="_blank" rel="noopener" href="https://redis.io/commands">https://redis.io/commands</a> ）可以查看到不同的命令：</p>
<p><img src="/assets/1652887648826.png" class="lazyload" data-srcset="/assets/1652887648826.png" srcset="" alt="1652887648826"></p>
<p>当然我们也可以通过Help命令来帮助我们去查看命令</p>
<p><img src="/assets/1652887748279.png" class="lazyload" data-srcset="/assets/1652887748279.png" srcset="" alt="1652887748279"></p>
<h3 id="4-2-Redis-通用命令"><a href="#4-2-Redis-通用命令" class="headerlink" title="4.2 Redis 通用命令"></a>4.2 Redis 通用命令</h3><p>通用指令是部分数据类型的，都可以使用的指令，常见的有：</p>
<ul>
<li>KEYS：查看符合模板的所有key</li>
<li>DEL：删除一个指定的key</li>
<li>EXISTS：判断key是否存在</li>
<li>EXPIRE：给一个key设置有效期，有效期到期时该key会被自动删除</li>
<li>TTL：查看一个KEY的剩余有效期</li>
</ul>
<p>通过help [command] 可以查看一个命令的具体用法，例如：</p>
<p><img src="/assets/1652887865189.png" class="lazyload" data-srcset="/assets/1652887865189.png" srcset="" alt="1652887865189"></p>
<p>课堂代码如下</p>
<ul>
<li>KEYS</li>
</ul>
<figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">127.0.0.1:6379&gt; keys *</span><br><span class="line">1) <span class="string">&quot;name&quot;</span></span><br><span class="line">2) <span class="string">&quot;age&quot;</span></span><br><span class="line">127.0.0.1:6379&gt;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查询以a开头的key</span></span><br><span class="line">127.0.0.1:6379&gt; keys a*</span><br><span class="line">1) <span class="string">&quot;age&quot;</span></span><br><span class="line">127.0.0.1:6379&gt;</span><br></pre></td></tr></table></figure>

<p><strong>贴心小提示：在生产环境下，不推荐使用keys 命令，因为这个命令在key过多的情况下，效率不高</strong></p>
<ul>
<li>DEL</li>
</ul>
<figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line">127.0.0.1:6379&gt; <span class="built_in">help</span> del</span><br><span class="line"></span><br><span class="line">  DEL key [key ...]</span><br><span class="line">  summary: Delete a key</span><br><span class="line">  since: 1.0.0</span><br><span class="line">  group: generic</span><br><span class="line"></span><br><span class="line">127.0.0.1:6379&gt; del name <span class="comment">#删除单个</span></span><br><span class="line">(<span class="built_in">integer</span>) 1  <span class="comment">#成功删除1个</span></span><br><span class="line"></span><br><span class="line">127.0.0.1:6379&gt; keys *</span><br><span class="line">1) <span class="string">&quot;age&quot;</span></span><br><span class="line"></span><br><span class="line">127.0.0.1:6379&gt; MSET k1 v1 k2 v2 k3 v3 <span class="comment">#批量添加数据</span></span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">127.0.0.1:6379&gt; keys *</span><br><span class="line">1) <span class="string">&quot;k3&quot;</span></span><br><span class="line">2) <span class="string">&quot;k2&quot;</span></span><br><span class="line">3) <span class="string">&quot;k1&quot;</span></span><br><span class="line">4) <span class="string">&quot;age&quot;</span></span><br><span class="line"></span><br><span class="line">127.0.0.1:6379&gt; del k1 k2 k3 k4</span><br><span class="line">(<span class="built_in">integer</span>) 3   <span class="comment">#此处返回的是成功删除的key，由于redis中只有k1,k2,k3 所以只成功删除3个，最终返回</span></span><br><span class="line">127.0.0.1:6379&gt;</span><br><span class="line"></span><br><span class="line">127.0.0.1:6379&gt; keys * <span class="comment">#再查询全部的key</span></span><br><span class="line">1) <span class="string">&quot;age&quot;</span>	<span class="comment">#只剩下一个了</span></span><br><span class="line">127.0.0.1:6379&gt;</span><br></pre></td></tr></table></figure>

<p><strong>贴心小提示：同学们在拷贝代码的时候，只需要拷贝对应的命令哦~</strong></p>
<ul>
<li>EXISTS</li>
</ul>
<figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">127.0.0.1:6379&gt; <span class="built_in">help</span> EXISTS</span><br><span class="line"></span><br><span class="line">  EXISTS key [key ...]</span><br><span class="line">  summary: Determine <span class="keyword">if</span> a key exists</span><br><span class="line">  since: 1.0.0</span><br><span class="line">  group: generic</span><br><span class="line"></span><br><span class="line">127.0.0.1:6379&gt; exists age</span><br><span class="line">(<span class="built_in">integer</span>) 1</span><br><span class="line"></span><br><span class="line">127.0.0.1:6379&gt; exists name</span><br><span class="line">(<span class="built_in">integer</span>) 0</span><br></pre></td></tr></table></figure>

<ul>
<li>EXPIRE</li>
</ul>
<p><strong>贴心小提示</strong>：内存非常宝贵，对于一些数据，我们应当给他一些过期时间，当过期时间到了之后，他就会自动被删除~</p>
<figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line">127.0.0.1:6379&gt; expire age 10</span><br><span class="line">(<span class="built_in">integer</span>) 1</span><br><span class="line"></span><br><span class="line">127.0.0.1:6379&gt; ttl age</span><br><span class="line">(<span class="built_in">integer</span>) 8</span><br><span class="line"></span><br><span class="line">127.0.0.1:6379&gt; ttl age</span><br><span class="line">(<span class="built_in">integer</span>) 6</span><br><span class="line"></span><br><span class="line">127.0.0.1:6379&gt; ttl age</span><br><span class="line">(<span class="built_in">integer</span>) -2</span><br><span class="line"></span><br><span class="line">127.0.0.1:6379&gt; ttl age</span><br><span class="line">(<span class="built_in">integer</span>) -2  <span class="comment">#当这个key过期了，那么此时查询出来就是-2 </span></span><br><span class="line"></span><br><span class="line">127.0.0.1:6379&gt; keys *</span><br><span class="line">(empty list or <span class="built_in">set</span>)</span><br><span class="line"></span><br><span class="line">127.0.0.1:6379&gt; <span class="built_in">set</span> age 10 <span class="comment">#如果没有设置过期时间</span></span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">127.0.0.1:6379&gt; ttl age</span><br><span class="line">(<span class="built_in">integer</span>) -1  <span class="comment"># ttl的返回值就是-1</span></span><br></pre></td></tr></table></figure>



<h3 id="4-3-Redis命令-String命令"><a href="#4-3-Redis命令-String命令" class="headerlink" title="4.3 Redis命令-String命令"></a>4.3 Redis命令-String命令</h3><p>String类型，也就是字符串类型，是Redis中最简单的存储类型。</p>
<p>其value是字符串，不过根据字符串的格式不同，又可以分为3类：</p>
<ul>
<li>string：普通字符串</li>
<li>int：整数类型，可以做自增.自减操作</li>
<li>float：浮点类型，可以做自增.自减操作</li>
</ul>
<p><img src="/assets/1652890121291.png" class="lazyload" data-srcset="/assets/1652890121291.png" srcset="" alt="1652890121291"></p>
<p>String的常见命令有：</p>
<ul>
<li>SET：添加或者修改已经存在的一个String类型的键值对</li>
<li>GET：根据key获取String类型的value</li>
<li>MSET：批量添加多个String类型的键值对</li>
<li>MGET：根据多个key获取多个String类型的value</li>
<li>INCR：让一个整型的key自增1</li>
<li>INCRBY:让一个整型的key自增并指定步长，例如：incrby num 2 让num值自增2</li>
<li>INCRBYFLOAT：让一个浮点类型的数字自增并指定步长</li>
<li>SETNX：添加一个String类型的键值对，前提是这个key不存在，否则不执行</li>
<li>SETEX：添加一个String类型的键值对，并且指定有效期</li>
</ul>
<p><strong>贴心小提示</strong>：以上命令除了INCRBYFLOAT 都是常用命令</p>
<ul>
<li>SET 和GET: 如果key不存在则是新增，如果存在则是修改</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span>&gt; set name Rose  <span class="comment">//原来不存在</span></span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line"><span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span>&gt; get name </span><br><span class="line"><span class="string">&quot;Rose&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span>&gt; set name Jack <span class="comment">//原来存在，就是修改</span></span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line"><span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span>&gt; get name</span><br><span class="line"><span class="string">&quot;Jack&quot;</span></span><br></pre></td></tr></table></figure>

<ul>
<li>MSET和MGET</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span>&gt; MSET k1 v1 k2 v2 k3 v3</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line"><span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span>&gt; MGET name age k1 k2 k3</span><br><span class="line"><span class="number">1</span>) <span class="string">&quot;Jack&quot;</span> <span class="comment">//之前存在的name</span></span><br><span class="line"><span class="number">2</span>) <span class="string">&quot;10&quot;</span>   <span class="comment">//之前存在的age</span></span><br><span class="line"><span class="number">3</span>) <span class="string">&quot;v1&quot;</span></span><br><span class="line"><span class="number">4</span>) <span class="string">&quot;v2&quot;</span></span><br><span class="line"><span class="number">5</span>) <span class="string">&quot;v3&quot;</span></span><br></pre></td></tr></table></figure>

<ul>
<li>INCR和INCRBY和DECY</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span>&gt; get age </span><br><span class="line"><span class="string">&quot;10&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span>&gt; incr age <span class="comment">//增加1</span></span><br><span class="line">(integer) <span class="number">11</span></span><br><span class="line">    </span><br><span class="line"><span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span>&gt; get age <span class="comment">//获得age</span></span><br><span class="line"><span class="string">&quot;11&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span>&gt; incrby age <span class="number">2</span> <span class="comment">//一次增加2</span></span><br><span class="line">(integer) <span class="number">13</span> <span class="comment">//返回目前的age的值</span></span><br><span class="line">    </span><br><span class="line"><span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span>&gt; incrby age <span class="number">2</span></span><br><span class="line">(integer) <span class="number">15</span></span><br><span class="line">    </span><br><span class="line"><span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span>&gt; incrby age -<span class="number">1</span> <span class="comment">//也可以增加负数，相当于减</span></span><br><span class="line">(integer) <span class="number">14</span></span><br><span class="line">    </span><br><span class="line"><span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span>&gt; incrby age -<span class="number">2</span> <span class="comment">//一次减少2个</span></span><br><span class="line">(integer) <span class="number">12</span></span><br><span class="line">    </span><br><span class="line"><span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span>&gt; DECR age <span class="comment">//相当于 incr 负数，减少正常用法</span></span><br><span class="line">(integer) <span class="number">11</span></span><br><span class="line">    </span><br><span class="line"><span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span>&gt; get age </span><br><span class="line"><span class="string">&quot;11&quot;</span></span><br><span class="line"></span><br></pre></td></tr></table></figure>

<ul>
<li>SETNX</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span>&gt; help setnx</span><br><span class="line"></span><br><span class="line">  SETNX key value</span><br><span class="line">  summary: Set the value of a key, only <span class="keyword">if</span> the key does not exist</span><br><span class="line">  since: <span class="number">1.0</span><span class="number">.0</span></span><br><span class="line">  group: string</span><br><span class="line"></span><br><span class="line"><span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span>&gt; set name Jack  <span class="comment">//设置名称</span></span><br><span class="line">OK</span><br><span class="line"><span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span>&gt; setnx name lisi <span class="comment">//如果key不存在，则添加成功</span></span><br><span class="line">(integer) <span class="number">0</span></span><br><span class="line"><span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span>&gt; get name <span class="comment">//由于name已经存在，所以lisi的操作失败</span></span><br><span class="line"><span class="string">&quot;Jack&quot;</span></span><br><span class="line"><span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span>&gt; setnx name2 lisi <span class="comment">//name2 不存在，所以操作成功</span></span><br><span class="line">(integer) <span class="number">1</span></span><br><span class="line"><span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span>&gt; get name2 </span><br><span class="line"><span class="string">&quot;lisi&quot;</span></span><br></pre></td></tr></table></figure>

<ul>
<li>SETEX</li>
</ul>
<figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">127.0.0.1:6379&gt; setex name 10 jack</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">127.0.0.1:6379&gt; ttl name</span><br><span class="line">(<span class="built_in">integer</span>) 8</span><br><span class="line"></span><br><span class="line">127.0.0.1:6379&gt; ttl name</span><br><span class="line">(<span class="built_in">integer</span>) 7</span><br><span class="line"></span><br><span class="line">127.0.0.1:6379&gt; ttl name</span><br><span class="line">(<span class="built_in">integer</span>) 5</span><br></pre></td></tr></table></figure>



<h3 id="4-4-Redis命令-Key的层级结构"><a href="#4-4-Redis命令-Key的层级结构" class="headerlink" title="4.4 Redis命令-Key的层级结构"></a>4.4 Redis命令-Key的层级结构</h3><p>Redis没有类似MySQL中的Table的概念，我们该如何区分不同类型的key呢？</p>
<p>例如，需要存储用户.商品信息到redis，有一个用户id是1，有一个商品id恰好也是1，此时如果使用id作为key，那就会冲突了，该怎么办？</p>
<p>我们可以通过给key添加前缀加以区分，不过这个前缀不是随便加的，有一定的规范：</p>
<p>Redis的key允许有多个单词形成层级结构，多个单词之间用’:’隔开，格式如下：</p>
<p><img src="/assets/1652941631682.png" class="lazyload" data-srcset="/assets/1652941631682.png" srcset="" alt="1652941631682"></p>
<p>这个格式并非固定，也可以根据自己的需求来删除或添加词条。</p>
<p>例如我们的项目名称叫 heima，有user和product两种不同类型的数据，我们可以这样定义key：</p>
<ul>
<li><p>user相关的key：<strong>heima:user:1</strong></p>
</li>
<li><p>product相关的key：<strong>heima:product:1</strong></p>
</li>
</ul>
<p>如果Value是一个Java对象，例如一个User对象，则可以将对象序列化为JSON字符串后存储：</p>
<table>
<thead>
<tr>
<th><strong>KEY</strong></th>
<th><strong>VALUE</strong></th>
</tr>
</thead>
<tbody><tr>
<td>heima:user:1</td>
<td>{“id”:1, “name”: “Jack”, “age”: 21}</td>
</tr>
<tr>
<td>heima:product:1</td>
<td>{“id”:1, “name”: “小米11”, “price”: 4999}</td>
</tr>
</tbody></table>
<p>一旦我们向redis采用这样的方式存储，那么在可视化界面中，redis会以层级结构来进行存储，形成类似于这样的结构，更加方便Redis获取数据</p>
<p><img src="/assets/1652941883537.png" class="lazyload" data-srcset="/assets/1652941883537.png" srcset="" alt="1652941883537"></p>
<h3 id="4-5-Redis命令-Hash命令"><a href="#4-5-Redis命令-Hash命令" class="headerlink" title="4.5 Redis命令-Hash命令"></a>4.5 Redis命令-Hash命令</h3><p>Hash类型，也叫散列，其value是一个无序字典，类似于Java中的HashMap结构。</p>
<p>String结构是将对象序列化为JSON字符串后存储，当需要修改对象某个字段时很不方便：</p>
<p><img src="/assets/1652941995945.png" class="lazyload" data-srcset="/assets/1652941995945.png" srcset="" alt="1652941995945"></p>
<p>Hash结构可以将对象中的每个字段独立存储，可以针对单个字段做CRUD：</p>
<p><img src="/assets/1652942027719.png" class="lazyload" data-srcset="/assets/1652942027719.png" srcset="" alt="1652942027719"></p>
<p><img src="F:\JAVA笔记库\img\151515.png" class="lazyload" data-srcset="F:\JAVA笔记库\img\151515.png" srcset=""></p>
<p><strong>Hash类型的常见命令</strong></p>
<ul>
<li><p>HSET key field value：添加或者修改hash类型key的field的值</p>
</li>
<li><p>HGET key field：获取一个hash类型key的field的值</p>
</li>
<li><p>HMSET：批量添加多个hash类型key的field的值</p>
</li>
<li><p>HMGET：批量获取多个hash类型key的field的值</p>
</li>
<li><p>HGETALL：获取一个hash类型的key中的所有的field和value</p>
</li>
<li><p>HKEYS：获取一个hash类型的key中的所有的field</p>
</li>
<li><p>HINCRBY:让一个hash类型key的字段值自增并指定步长</p>
</li>
<li><p>HSETNX：添加一个hash类型的key的field值，前提是这个field不存在，否则不执行</p>
</li>
</ul>
<p><strong>贴心小提示</strong>：哈希结构也是我们以后实际开发中常用的命令哟</p>
<ul>
<li>HSET和HGET</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span>&gt; HSET heima:user:<span class="number">3</span> name Lucy<span class="comment">//大key是 heima:user:3 小key是name，小value是Lucy</span></span><br><span class="line">(integer) <span class="number">1</span></span><br><span class="line"><span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span>&gt; HSET heima:user:<span class="number">3</span> age <span class="number">21</span><span class="comment">// 如果操作不存在的数据，则是新增</span></span><br><span class="line">(integer) <span class="number">1</span></span><br><span class="line"><span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span>&gt; HSET heima:user:<span class="number">3</span> age <span class="number">17</span> <span class="comment">//如果操作存在的数据，则是修改</span></span><br><span class="line">(integer) <span class="number">0</span></span><br><span class="line"><span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span>&gt; HGET heima:user:<span class="number">3</span> name </span><br><span class="line"><span class="string">&quot;Lucy&quot;</span></span><br><span class="line"><span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span>&gt; HGET heima:user:<span class="number">3</span> age</span><br><span class="line"><span class="string">&quot;17&quot;</span></span><br></pre></td></tr></table></figure>

<ul>
<li>HMSET和HMGET</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span>&gt; HMSET heima:user:<span class="number">4</span> name HanMeiMei</span><br><span class="line">OK</span><br><span class="line"><span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span>&gt; HMSET heima:user:<span class="number">4</span> name LiLei age <span class="number">20</span> sex man</span><br><span class="line">OK</span><br><span class="line"><span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span>&gt; HMGET heima:user:<span class="number">4</span> name age sex</span><br><span class="line"><span class="number">1</span>) <span class="string">&quot;LiLei&quot;</span></span><br><span class="line"><span class="number">2</span>) <span class="string">&quot;20&quot;</span></span><br><span class="line"><span class="number">3</span>) <span class="string">&quot;man&quot;</span></span><br></pre></td></tr></table></figure>

<ul>
<li>HGETALL</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span>&gt; HGETALL heima:user:<span class="number">4</span></span><br><span class="line"><span class="number">1</span>) <span class="string">&quot;name&quot;</span></span><br><span class="line"><span class="number">2</span>) <span class="string">&quot;LiLei&quot;</span></span><br><span class="line"><span class="number">3</span>) <span class="string">&quot;age&quot;</span></span><br><span class="line"><span class="number">4</span>) <span class="string">&quot;20&quot;</span></span><br><span class="line"><span class="number">5</span>) <span class="string">&quot;sex&quot;</span></span><br><span class="line"><span class="number">6</span>) <span class="string">&quot;man&quot;</span></span><br></pre></td></tr></table></figure>

<ul>
<li>HKEYS和HVALS</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span>&gt; HKEYS heima:user:<span class="number">4</span></span><br><span class="line"><span class="number">1</span>) <span class="string">&quot;name&quot;</span></span><br><span class="line"><span class="number">2</span>) <span class="string">&quot;age&quot;</span></span><br><span class="line"><span class="number">3</span>) <span class="string">&quot;sex&quot;</span></span><br><span class="line"><span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span>&gt; HVALS heima:user:<span class="number">4</span></span><br><span class="line"><span class="number">1</span>) <span class="string">&quot;LiLei&quot;</span></span><br><span class="line"><span class="number">2</span>) <span class="string">&quot;20&quot;</span></span><br><span class="line"><span class="number">3</span>) <span class="string">&quot;man&quot;</span></span><br></pre></td></tr></table></figure>

<ul>
<li>HINCRBY</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span>&gt; HINCRBY  heima:user:<span class="number">4</span> age <span class="number">2</span></span><br><span class="line">(integer) <span class="number">22</span></span><br><span class="line"><span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span>&gt; HVALS heima:user:<span class="number">4</span></span><br><span class="line"><span class="number">1</span>) <span class="string">&quot;LiLei&quot;</span></span><br><span class="line"><span class="number">2</span>) <span class="string">&quot;22&quot;</span></span><br><span class="line"><span class="number">3</span>) <span class="string">&quot;man&quot;</span></span><br><span class="line"><span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span>&gt; HINCRBY  heima:user:<span class="number">4</span> age -<span class="number">2</span></span><br><span class="line">(integer) <span class="number">20</span></span><br></pre></td></tr></table></figure>

<ul>
<li>HSETNX</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span>&gt; HSETNX heima:user4 sex <span class="title function_">woman</span></span><br><span class="line"><span class="params">(integer)</span> <span class="number">1</span></span><br><span class="line"><span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span>&gt; HGETALL heima:user:<span class="number">3</span></span><br><span class="line"><span class="number">1</span>) <span class="string">&quot;name&quot;</span></span><br><span class="line"><span class="number">2</span>) <span class="string">&quot;Lucy&quot;</span></span><br><span class="line"><span class="number">3</span>) <span class="string">&quot;age&quot;</span></span><br><span class="line"><span class="number">4</span>) <span class="string">&quot;17&quot;</span></span><br><span class="line"><span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span>&gt; HSETNX heima:user:<span class="number">3</span> sex <span class="title function_">woman</span></span><br><span class="line"><span class="params">(integer)</span> <span class="number">1</span></span><br><span class="line"><span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span>&gt; HGETALL heima:user:<span class="number">3</span></span><br><span class="line"><span class="number">1</span>) <span class="string">&quot;name&quot;</span></span><br><span class="line"><span class="number">2</span>) <span class="string">&quot;Lucy&quot;</span></span><br><span class="line"><span class="number">3</span>) <span class="string">&quot;age&quot;</span></span><br><span class="line"><span class="number">4</span>) <span class="string">&quot;17&quot;</span></span><br><span class="line"><span class="number">5</span>) <span class="string">&quot;sex&quot;</span></span><br><span class="line"><span class="number">6</span>) <span class="string">&quot;woman&quot;</span></span><br></pre></td></tr></table></figure>

<h3 id="4-6-Redis命令-List命令"><a href="#4-6-Redis命令-List命令" class="headerlink" title="4.6 Redis命令-List命令"></a>4.6 Redis命令-List命令</h3><p>Redis中的List类型与Java中的LinkedList类似，可以看做是一个双向链表结构。既可以支持正向检索和也可以支持反向检索。</p>
<p>特征也与LinkedList类似：</p>
<ul>
<li>有序</li>
<li>元素可以重复</li>
<li>插入和删除快</li>
<li>查询速度一般</li>
</ul>
<p>常用来存储一个有序数据，例如：朋友圈点赞列表，评论列表等。</p>
<p><strong>List的常见命令有：</strong></p>
<ul>
<li>LPUSH key element … ：向列表左侧插入一个或多个元素</li>
<li>LPOP key：移除并返回列表左侧的第一个元素，没有则返回nil</li>
<li>RPUSH key element … ：向列表右侧插入一个或多个元素</li>
<li>RPOP key：移除并返回列表右侧的第一个元素</li>
<li>LRANGE key star end：返回一段角标范围内的所有元素</li>
<li>BLPOP和BRPOP：与LPOP和RPOP类似，只不过在没有元素时等待指定时间，而不是直接返回nil</li>
</ul>
<p><img src="/.%5CRedis.assets%5C1652943604992.png" class="lazyload" data-srcset="/.%5CRedis.assets%5C1652943604992.png" srcset="" alt="1652943604992"></p>
<ul>
<li>LPUSH和RPUSH</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span>&gt; LPUSH users <span class="number">1</span> <span class="number">2</span> <span class="number">3</span></span><br><span class="line">(integer) <span class="number">3</span></span><br><span class="line"><span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span>&gt; RPUSH users <span class="number">4</span> <span class="number">5</span> <span class="number">6</span></span><br><span class="line">(integer) <span class="number">6</span></span><br></pre></td></tr></table></figure>

<ul>
<li>LPOP和RPOP</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span>&gt; LPOP users</span><br><span class="line"><span class="string">&quot;3&quot;</span></span><br><span class="line"><span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span>&gt; RPOP users</span><br><span class="line"><span class="string">&quot;6&quot;</span></span><br></pre></td></tr></table></figure>

<ul>
<li>LRANGE</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span>&gt; LRANGE users <span class="number">1</span> <span class="number">2</span></span><br><span class="line"><span class="number">1</span>) <span class="string">&quot;1&quot;</span></span><br><span class="line"><span class="number">2</span>) <span class="string">&quot;4&quot;</span></span><br></pre></td></tr></table></figure>

<h3 id="4-7-Redis命令-Set命令"><a href="#4-7-Redis命令-Set命令" class="headerlink" title="4.7 Redis命令-Set命令"></a>4.7 Redis命令-Set命令</h3><p>Redis的Set结构与Java中的HashSet类似，可以看做是一个value为null的HashMap。因为也是一个hash表，因此具备与HashSet类似的特征：</p>
<ul>
<li>无序</li>
<li>元素不可重复</li>
<li>查找快</li>
<li>支持交集.并集.差集等功能</li>
</ul>
<p><strong>Set类型的常见命令</strong></p>
<ul>
<li>SADD key member … ：向set中添加一个或多个元素</li>
<li>SREM key member … : 移除set中的指定元素</li>
<li>SCARD key： 返回set中元素的个数</li>
<li>SISMEMBER key member：判断一个元素是否存在于set中</li>
<li>SMEMBERS：获取set中的所有元素</li>
<li>SINTER key1 key2 … ：求key1与key2的交集</li>
<li>SDIFF key1 key2 … ：求key1与key2的差集</li>
<li>SUNION key1 key2 ..：求key1和key2的并集</li>
</ul>
<p>例如两个集合：s1和s2:</p>
<p><img src="https://i.imgur.com/ha8x86R.png" class="lazyload" data-srcset="https://i.imgur.com/ha8x86R.png" srcset=""></p>
<p>求交集：SINTER s1 s2</p>
<p>求s1与s2的不同：SDIFF s1 s2</p>
<p><img src="https://i.imgur.com/L9vTv2X.png" class="lazyload" data-srcset="https://i.imgur.com/L9vTv2X.png" srcset=""></p>
<p><strong>具体命令</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span>&gt; sadd s1 a b <span class="title function_">c</span></span><br><span class="line"><span class="params">(integer)</span> <span class="number">3</span></span><br><span class="line"><span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span>&gt; smembers s1</span><br><span class="line"><span class="number">1</span>) <span class="string">&quot;c&quot;</span></span><br><span class="line"><span class="number">2</span>) <span class="string">&quot;b&quot;</span></span><br><span class="line"><span class="number">3</span>) <span class="string">&quot;a&quot;</span></span><br><span class="line"><span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span>&gt; srem s1 <span class="title function_">a</span></span><br><span class="line"><span class="params">(integer)</span> <span class="number">1</span></span><br><span class="line">    </span><br><span class="line"><span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span>&gt; SISMEMBER s1 <span class="title function_">a</span></span><br><span class="line"><span class="params">(integer)</span> <span class="number">0</span></span><br><span class="line">    </span><br><span class="line"><span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span>&gt; SISMEMBER s1 <span class="title function_">b</span></span><br><span class="line"><span class="params">(integer)</span> <span class="number">1</span></span><br><span class="line">    </span><br><span class="line"><span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span>&gt; SCARD <span class="title function_">s1</span></span><br><span class="line"><span class="params">(integer)</span> <span class="number">2</span></span><br></pre></td></tr></table></figure>

<p><strong>案例</strong></p>
<ul>
<li>将下列数据用Redis的Set集合来存储：</li>
<li>张三的好友有：李四.王五.赵六</li>
<li>李四的好友有：王五.麻子.二狗</li>
<li>利用Set的命令实现下列功能：</li>
<li>计算张三的好友有几人</li>
<li>计算张三和李四有哪些共同好友</li>
<li>查询哪些人是张三的好友却不是李四的好友</li>
<li>查询张三和李四的好友总共有哪些人</li>
<li>判断李四是否是张三的好友</li>
<li>判断张三是否是李四的好友</li>
<li>将李四从张三的好友列表中移除</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span>&gt; SADD zs lisi wangwu <span class="title function_">zhaoliu</span></span><br><span class="line"><span class="params">(integer)</span> <span class="number">3</span></span><br><span class="line">    </span><br><span class="line"><span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span>&gt; SADD ls wangwu mazi <span class="title function_">ergou</span></span><br><span class="line"><span class="params">(integer)</span> <span class="number">3</span></span><br><span class="line">    </span><br><span class="line"><span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span>&gt; SCARD <span class="title function_">zs</span></span><br><span class="line"><span class="params">(integer)</span> <span class="number">3</span></span><br><span class="line">    </span><br><span class="line"><span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span>&gt; SINTER zs ls</span><br><span class="line"><span class="number">1</span>) <span class="string">&quot;wangwu&quot;</span></span><br><span class="line">    </span><br><span class="line"><span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span>&gt; SDIFF zs ls</span><br><span class="line"><span class="number">1</span>) <span class="string">&quot;zhaoliu&quot;</span></span><br><span class="line"><span class="number">2</span>) <span class="string">&quot;lisi&quot;</span></span><br><span class="line">    </span><br><span class="line"><span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span>&gt; SUNION zs ls</span><br><span class="line"><span class="number">1</span>) <span class="string">&quot;wangwu&quot;</span></span><br><span class="line"><span class="number">2</span>) <span class="string">&quot;zhaoliu&quot;</span></span><br><span class="line"><span class="number">3</span>) <span class="string">&quot;lisi&quot;</span></span><br><span class="line"><span class="number">4</span>) <span class="string">&quot;mazi&quot;</span></span><br><span class="line"><span class="number">5</span>) <span class="string">&quot;ergou&quot;</span></span><br><span class="line">    </span><br><span class="line"><span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span>&gt; SISMEMBER zs <span class="title function_">lisi</span></span><br><span class="line"><span class="params">(integer)</span> <span class="number">1</span></span><br><span class="line">    </span><br><span class="line"><span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span>&gt; SISMEMBER ls <span class="title function_">zhangsan</span></span><br><span class="line"><span class="params">(integer)</span> <span class="number">0</span></span><br><span class="line">    </span><br><span class="line"><span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span>&gt; SREM zs <span class="title function_">lisi</span></span><br><span class="line"><span class="params">(integer)</span> <span class="number">1</span></span><br><span class="line">    </span><br><span class="line"><span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">6379</span>&gt; SMEMBERS zs</span><br><span class="line"><span class="number">1</span>) <span class="string">&quot;zhaoliu&quot;</span></span><br><span class="line"><span class="number">2</span>) <span class="string">&quot;wangwu&quot;</span></span><br></pre></td></tr></table></figure>

<h3 id="4-8-Redis命令-SortedSet类型"><a href="#4-8-Redis命令-SortedSet类型" class="headerlink" title="4.8 Redis命令-SortedSet类型"></a>4.8 Redis命令-SortedSet类型</h3><p>Redis的SortedSet是一个可排序的set集合，与Java中的TreeSet有些类似，但底层数据结构却差别很大。SortedSet中的每一个元素都带有一个score属性，可以基于score属性对元素排序，底层的实现是一个跳表（SkipList）加 hash表。</p>
<p>SortedSet具备下列特性：</p>
<ul>
<li>可排序</li>
<li>元素不重复</li>
<li>查询速度快</li>
</ul>
<p>因为SortedSet的可排序特性，经常被用来实现排行榜这样的功能。</p>
<p>SortedSet的常见命令有：</p>
<ul>
<li>ZADD key score member：添加一个或多个元素到sorted set ，如果已经存在则更新其score值</li>
<li>ZREM key member：删除sorted set中的一个指定元素</li>
<li>ZSCORE key member : 获取sorted set中的指定元素的score值</li>
<li>ZRANK key member：获取sorted set 中的指定元素的排名</li>
<li>ZCARD key：获取sorted set中的元素个数</li>
<li>ZCOUNT key min max：统计score值在给定范围内的所有元素的个数</li>
<li>ZINCRBY key increment member：让sorted set中的指定元素自增，步长为指定的increment值</li>
<li>ZRANGE key min max：按照score排序后，获取指定排名范围内的元素</li>
<li>ZRANGEBYSCORE key min max：按照score排序后，获取指定score范围内的元素</li>
<li>ZDIFF.ZINTER.ZUNION：求差集.交集.并集</li>
</ul>
<p>注意：所有的排名默认都是升序，如果要降序则在命令的Z后面添加REV即可，例如：</p>
<ul>
<li><strong>升序</strong>获取sorted set 中的指定元素的排名：ZRANK key member</li>
<li><strong>降序</strong>获取sorted set 中的指定元素的排名：ZREVRANK key memeber</li>
</ul>
<h2 id="5-Redis的Java客户端-Jedis"><a href="#5-Redis的Java客户端-Jedis" class="headerlink" title="5.Redis的Java客户端-Jedis"></a>5.Redis的Java客户端-Jedis</h2><p>在Redis官网中提供了各种语言的客户端，地址：<a target="_blank" rel="noopener" href="https://redis.io/docs/clients/">https://redis.io/docs/clients/</a></p>
<p><img src="https://i.imgur.com/9f68ivq.png" class="lazyload" data-srcset="https://i.imgur.com/9f68ivq.png" srcset=""></p>
<p>其中Java客户端也包含很多：</p>
<p><img src="/assets/image-20220609102817435.png" class="lazyload" data-srcset="/assets/image-20220609102817435.png" srcset="" alt="image-20220609102817435"></p>
<p>标记为❤的就是推荐使用的java客户端，包括：</p>
<ul>
<li>Jedis和Lettuce：这两个主要是提供了Redis命令对应的API，方便我们操作Redis，而SpringDataRedis又对这两种做了抽象和封装，因此我们后期会直接以SpringDataRedis来学习。</li>
<li>Redisson：是在Redis基础上实现了分布式的可伸缩的java数据结构，例如Map.Queue等，而且支持跨进程的同步机制：Lock.Semaphore等待，比较适合用来实现特殊的功能需求。</li>
</ul>
<h3 id="5-1-Jedis快速入门"><a href="#5-1-Jedis快速入门" class="headerlink" title="5.1 Jedis快速入门"></a>5.1 Jedis快速入门</h3><p><strong>入门案例详细步骤</strong></p>
<p>案例分析：</p>
<p>0）创建工程：</p>
<p><img src="/%5CRedis.assets%5C1652959239813.png" class="lazyload" data-srcset="/%5CRedis.assets%5C1652959239813.png" srcset="" alt="1652959239813"></p>
<p>1）引入依赖：</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!--jedis--&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>redis.clients<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>jedis<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>3.7.0<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="comment">&lt;!--单元测试--&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.junit.jupiter<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>junit-jupiter<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>5.7.0<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">scope</span>&gt;</span>test<span class="tag">&lt;/<span class="name">scope</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure>



<p>2）建立连接</p>
<p>新建一个单元测试类，内容如下：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> Jedis jedis;</span><br><span class="line"></span><br><span class="line"><span class="meta">@BeforeEach</span></span><br><span class="line"><span class="keyword">void</span> <span class="title function_">setUp</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="comment">// 1.建立连接</span></span><br><span class="line">    <span class="comment">// jedis = new Jedis(&quot;192.168.150.101&quot;, 6379);</span></span><br><span class="line">    jedis = JedisConnectionFactory.getJedis();</span><br><span class="line">    <span class="comment">// 2.设置密码</span></span><br><span class="line">    jedis.auth(<span class="string">&quot;123321&quot;</span>);</span><br><span class="line">    <span class="comment">// 3.选择库</span></span><br><span class="line">    jedis.select(<span class="number">0</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>3）测试：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Test</span></span><br><span class="line"><span class="keyword">void</span> <span class="title function_">testString</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="comment">// 存入数据</span></span><br><span class="line">    <span class="type">String</span> <span class="variable">result</span> <span class="operator">=</span> jedis.set(<span class="string">&quot;name&quot;</span>, <span class="string">&quot;虎哥&quot;</span>);</span><br><span class="line">    System.out.println(<span class="string">&quot;result = &quot;</span> + result);</span><br><span class="line">    <span class="comment">// 获取数据</span></span><br><span class="line">    <span class="type">String</span> <span class="variable">name</span> <span class="operator">=</span> jedis.get(<span class="string">&quot;name&quot;</span>);</span><br><span class="line">    System.out.println(<span class="string">&quot;name = &quot;</span> + name);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Test</span></span><br><span class="line"><span class="keyword">void</span> <span class="title function_">testHash</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="comment">// 插入hash数据</span></span><br><span class="line">    jedis.hset(<span class="string">&quot;user:1&quot;</span>, <span class="string">&quot;name&quot;</span>, <span class="string">&quot;Jack&quot;</span>);</span><br><span class="line">    jedis.hset(<span class="string">&quot;user:1&quot;</span>, <span class="string">&quot;age&quot;</span>, <span class="string">&quot;21&quot;</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取</span></span><br><span class="line">    Map&lt;String, String&gt; map = jedis.hgetAll(<span class="string">&quot;user:1&quot;</span>);</span><br><span class="line">    System.out.println(map);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>4）释放资源</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@AfterEach</span></span><br><span class="line"><span class="keyword">void</span> <span class="title function_">tearDown</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (jedis != <span class="literal">null</span>) &#123;</span><br><span class="line">        jedis.close();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>





<h3 id="5-2-Jedis连接池"><a href="#5-2-Jedis连接池" class="headerlink" title="5.2 Jedis连接池"></a>5.2 Jedis连接池</h3><p>Jedis本身是线程不安全的，并且频繁的创建和销毁连接会有性能损耗，因此我们推荐大家使用Jedis连接池代替Jedis的直连方式</p>
<p>有关池化思想，并不仅仅是这里会使用，很多地方都有，比如说我们的数据库连接池，比如我们tomcat中的线程池，这些都是池化思想的体现。</p>
<h4 id="5-2-1-创建Jedis的连接池"><a href="#5-2-1-创建Jedis的连接池" class="headerlink" title="5.2.1.创建Jedis的连接池"></a>5.2.1.创建Jedis的连接池</h4><p>- </p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">JedisConnectionFacotry</span> &#123;</span><br><span class="line"></span><br><span class="line">     <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> JedisPool jedisPool;</span><br><span class="line"></span><br><span class="line">     <span class="keyword">static</span> &#123;</span><br><span class="line">         <span class="comment">//配置连接池</span></span><br><span class="line">         <span class="type">JedisPoolConfig</span> <span class="variable">poolConfig</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">JedisPoolConfig</span>();</span><br><span class="line">         poolConfig.setMaxTotal(<span class="number">8</span>);</span><br><span class="line">         poolConfig.setMaxIdle(<span class="number">8</span>);</span><br><span class="line">         poolConfig.setMinIdle(<span class="number">0</span>);</span><br><span class="line">         poolConfig.setMaxWaitMillis(<span class="number">1000</span>);</span><br><span class="line">         <span class="comment">//创建连接池对象</span></span><br><span class="line">         jedisPool = <span class="keyword">new</span> <span class="title class_">JedisPool</span>(poolConfig,</span><br><span class="line">                 <span class="string">&quot;192.168.150.101&quot;</span>,<span class="number">6379</span>,<span class="number">1000</span>,<span class="string">&quot;123321&quot;</span>);</span><br><span class="line">     &#125;</span><br><span class="line"></span><br><span class="line">     <span class="keyword">public</span> <span class="keyword">static</span> Jedis <span class="title function_">getJedis</span><span class="params">()</span>&#123;</span><br><span class="line">          <span class="keyword">return</span> jedisPool.getResource();</span><br><span class="line">     &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>代码说明：</strong></p>
<ul>
<li><p>1） JedisConnectionFacotry：工厂设计模式是实际开发中非常常用的一种设计模式，我们可以使用工厂，去降低代的耦合，比如Spring中的Bean的创建，就用到了工厂设计模式</p>
</li>
<li><p>2）静态代码块：随着类的加载而加载，确保只能执行一次，我们在加载当前工厂类的时候，就可以执行static的操作完成对 连接池的初始化</p>
</li>
<li><p>3）最后提供返回连接池中连接的方法.</p>
</li>
</ul>
<h4 id="5-2-2-改造原始代码"><a href="#5-2-2-改造原始代码" class="headerlink" title="5.2.2.改造原始代码"></a>5.2.2.改造原始代码</h4><p><strong>代码说明:</strong></p>
<p>1.在我们完成了使用工厂设计模式来完成代码的编写之后，我们在获得连接时，就可以通过工厂来获得。</p>
<p>，而不用直接去new对象，降低耦合，并且使用的还是连接池对象。</p>
<p>2.当我们使用了连接池后，当我们关闭连接其实并不是关闭，而是将Jedis还回连接池的。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"> <span class="meta">@BeforeEach</span></span><br><span class="line"> <span class="keyword">void</span> <span class="title function_">setUp</span><span class="params">()</span>&#123;</span><br><span class="line">     <span class="comment">//建立连接</span></span><br><span class="line">     <span class="comment">/*jedis = new Jedis(&quot;127.0.0.1&quot;,6379);*/</span></span><br><span class="line">     jedis = JedisConnectionFacotry.getJedis();</span><br><span class="line">      <span class="comment">//选择库</span></span><br><span class="line">     jedis.select(<span class="number">0</span>);</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@AfterEach</span></span><br><span class="line"> <span class="keyword">void</span> <span class="title function_">tearDown</span><span class="params">()</span> &#123;</span><br><span class="line">     <span class="keyword">if</span> (jedis != <span class="literal">null</span>) &#123;</span><br><span class="line">         jedis.close();</span><br><span class="line">     &#125;</span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure>



<h2 id="6-Redis的Java客户端-SpringDataRedis"><a href="#6-Redis的Java客户端-SpringDataRedis" class="headerlink" title="6.Redis的Java客户端-SpringDataRedis"></a>6.Redis的Java客户端-SpringDataRedis</h2><p>SpringData是Spring中数据操作的模块，包含对各种数据库的集成，其中对Redis的集成模块就叫做SpringDataRedis，官网地址：<a target="_blank" rel="noopener" href="https://spring.io/projects/spring-data-redis">https://spring.io/projects/spring-data-redis</a></p>
<ul>
<li>提供了对不同Redis客户端的整合（Lettuce和Jedis）</li>
<li>提供了RedisTemplate统一API来操作Redis</li>
<li>支持Redis的发布订阅模型</li>
<li>支持Redis哨兵和Redis集群</li>
<li>支持基于Lettuce的响应式编程</li>
<li>支持基于JDK.JSON.字符串.Spring对象的数据序列化及反序列化</li>
<li>支持基于Redis的JDKCollection实现</li>
</ul>
<p>SpringDataRedis中提供了RedisTemplate工具类，其中封装了各种对Redis的操作。并且将不同数据类型的操作API封装到了不同的类型中：</p>
<p><img src="/.%5CRedis.assets%5C1652976773295.png" class="lazyload" data-srcset="/.%5CRedis.assets%5C1652976773295.png" srcset="" alt="1652976773295"></p>
<h3 id="6-1-快速入门"><a href="#6-1-快速入门" class="headerlink" title="6.1.快速入门"></a>6.1.快速入门</h3><p>SpringBoot已经提供了对SpringDataRedis的支持，使用非常简单：</p>
<h4 id="6-1-1-导入pom坐标"><a href="#6-1-1-导入pom坐标" class="headerlink" title="6.1.1.导入pom坐标"></a>6.1.1.导入pom坐标</h4><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version=<span class="string">&quot;1.0&quot;</span> encoding=<span class="string">&quot;UTF-8&quot;</span>?&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">project</span> <span class="attr">xmlns</span>=<span class="string">&quot;http://maven.apache.org/POM/4.0.0&quot;</span> <span class="attr">xmlns:xsi</span>=<span class="string">&quot;http://www.w3.org/2001/XMLSchema-instance&quot;</span></span></span><br><span class="line"><span class="tag">         <span class="attr">xsi:schemaLocation</span>=<span class="string">&quot;http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">modelVersion</span>&gt;</span>4.0.0<span class="tag">&lt;/<span class="name">modelVersion</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">parent</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-parent<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">version</span>&gt;</span>2.5.7<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">relativePath</span>/&gt;</span> <span class="comment">&lt;!-- lookup parent from repository --&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">parent</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>com.heima<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>redis-demo<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>0.0.1-SNAPSHOT<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">name</span>&gt;</span>redis-demo<span class="tag">&lt;/<span class="name">name</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">description</span>&gt;</span>Demo project for Spring Boot<span class="tag">&lt;/<span class="name">description</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">properties</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">java.version</span>&gt;</span>1.8<span class="tag">&lt;/<span class="name">java.version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">properties</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependencies</span>&gt;</span></span><br><span class="line">        <span class="comment">&lt;!--redis依赖--&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-data-redis<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="comment">&lt;!--common-pool--&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.apache.commons<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>commons-pool2<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="comment">&lt;!--Jackson依赖--&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>com.fasterxml.jackson.core<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>jackson-databind<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.projectlombok<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>lombok<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">optional</span>&gt;</span>true<span class="tag">&lt;/<span class="name">optional</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-test<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">scope</span>&gt;</span>test<span class="tag">&lt;/<span class="name">scope</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependencies</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">build</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">plugins</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">plugin</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-maven-plugin<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">configuration</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">excludes</span>&gt;</span></span><br><span class="line">                        <span class="tag">&lt;<span class="name">exclude</span>&gt;</span></span><br><span class="line">                            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.projectlombok<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">                            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>lombok<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">                        <span class="tag">&lt;/<span class="name">exclude</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;/<span class="name">excludes</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;/<span class="name">configuration</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;/<span class="name">plugin</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">plugins</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">build</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;/<span class="name">project</span>&gt;</span></span><br></pre></td></tr></table></figure>

<h4 id="6-1-2-配置文件"><a href="#6-1-2-配置文件" class="headerlink" title="6.1.2 .配置文件"></a>6.1.2 .配置文件</h4><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">redis:</span></span><br><span class="line">    <span class="attr">host:</span> <span class="number">192.168</span><span class="number">.150</span><span class="number">.101</span></span><br><span class="line">    <span class="attr">port:</span> <span class="number">6379</span></span><br><span class="line">    <span class="attr">password:</span> <span class="number">123321</span></span><br><span class="line">    <span class="attr">lettuce:</span></span><br><span class="line">      <span class="attr">pool:</span></span><br><span class="line">        <span class="attr">max-active:</span> <span class="number">8</span>  <span class="comment">#最大连接</span></span><br><span class="line">        <span class="attr">max-idle:</span> <span class="number">8</span>   <span class="comment">#最大空闲连接</span></span><br><span class="line">        <span class="attr">min-idle:</span> <span class="number">0</span>   <span class="comment">#最小空闲连接</span></span><br><span class="line">        <span class="attr">max-wait:</span> <span class="string">100ms</span> <span class="comment">#连接等待时间</span></span><br></pre></td></tr></table></figure>

<h4 id="6-1-3-测试代码"><a href="#6-1-3-测试代码" class="headerlink" title="6.1.3.测试代码"></a>6.1.3.测试代码</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@SpringBootTest</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">RedisDemoApplicationTests</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> RedisTemplate&lt;String, Object&gt; redisTemplate;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">testString</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="comment">// 写入一条String数据</span></span><br><span class="line">        redisTemplate.opsForValue().set(<span class="string">&quot;name&quot;</span>, <span class="string">&quot;虎哥&quot;</span>);</span><br><span class="line">        <span class="comment">// 获取string数据</span></span><br><span class="line">        <span class="type">Object</span> <span class="variable">name</span> <span class="operator">=</span> redisTemplate.opsForValue().get(<span class="string">&quot;name&quot;</span>);</span><br><span class="line">        System.out.println(<span class="string">&quot;name = &quot;</span> + name);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>贴心小提示：SpringDataJpa使用起来非常简单，记住如下几个步骤即可</strong></p>
<p>SpringDataRedis的使用步骤：</p>
<ul>
<li>引入spring-boot-starter-data-redis依赖</li>
<li>在application.yml配置Redis信息</li>
<li>注入RedisTemplate</li>
</ul>
<h3 id="6-2-数据序列化器"><a href="#6-2-数据序列化器" class="headerlink" title="6.2 .数据序列化器"></a>6.2 .数据序列化器</h3><p>RedisTemplate可以接收任意Object作为值写入Redis：</p>
<p><img src="https://i.imgur.com/OEMcbuu.png" class="lazyload" data-srcset="https://i.imgur.com/OEMcbuu.png" srcset=""></p>
<p>只不过写入前会把Object序列化为字节形式，默认是采用JDK序列化，得到的结果是这样的：</p>
<p><img src="https://i.imgur.com/5FjtWk5.png" class="lazyload" data-srcset="https://i.imgur.com/5FjtWk5.png" srcset=""></p>
<p>缺点：</p>
<ul>
<li>可读性差</li>
<li>内存占用较大</li>
</ul>
<p>我们可以自定义RedisTemplate的序列化方式，代码如下：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">RedisConfig</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> RedisTemplate&lt;String, Object&gt; <span class="title function_">redisTemplate</span><span class="params">(RedisConnectionFactory connectionFactory)</span>&#123;</span><br><span class="line">        <span class="comment">// 创建RedisTemplate对象</span></span><br><span class="line">        RedisTemplate&lt;String, Object&gt; template = <span class="keyword">new</span> <span class="title class_">RedisTemplate</span>&lt;&gt;();</span><br><span class="line">        <span class="comment">// 设置连接工厂</span></span><br><span class="line">        template.setConnectionFactory(connectionFactory);</span><br><span class="line">        <span class="comment">// 创建JSON序列化工具</span></span><br><span class="line">        <span class="type">GenericJackson2JsonRedisSerializer</span> <span class="variable">jsonRedisSerializer</span> <span class="operator">=</span> </span><br><span class="line">            							<span class="keyword">new</span> <span class="title class_">GenericJackson2JsonRedisSerializer</span>();</span><br><span class="line">        <span class="comment">// 设置Key的序列化</span></span><br><span class="line">        template.setKeySerializer(RedisSerializer.string());</span><br><span class="line">        template.setHashKeySerializer(RedisSerializer.string());</span><br><span class="line">        <span class="comment">// 设置Value的序列化</span></span><br><span class="line">        template.setValueSerializer(jsonRedisSerializer);</span><br><span class="line">        template.setHashValueSerializer(jsonRedisSerializer);</span><br><span class="line">        <span class="comment">// 返回</span></span><br><span class="line">        <span class="keyword">return</span> template;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>这里采用了JSON序列化来代替默认的JDK序列化方式。最终结果如图：</p>
<p><img src="https://i.imgur.com/XOAq3cN.png" class="lazyload" data-srcset="https://i.imgur.com/XOAq3cN.png" srcset=""></p>
<p>整体可读性有了很大提升，并且能将Java对象自动的序列化为JSON字符串，并且查询时能自动把JSON反序列化为Java对象。不过，其中记录了序列化时对应的class名称，目的是为了查询时实现自动反序列化。这会带来额外的内存开销。</p>
<h3 id="6-3-StringRedisTemplate"><a href="#6-3-StringRedisTemplate" class="headerlink" title="6.3 StringRedisTemplate"></a>6.3 StringRedisTemplate</h3><p>尽管JSON的序列化方式可以满足我们的需求，但依然存在一些问题，如图：</p>
<p><img src="/.%5CRedis.assets%5C1653054602930.png" class="lazyload" data-srcset="/.%5CRedis.assets%5C1653054602930.png" srcset="" alt="1653054602930"></p>
<p>为了在反序列化时知道对象的类型，JSON序列化器会将类的class类型写入json结果中，存入Redis，会带来额外的内存开销。</p>
<p>为了减少内存的消耗，我们可以采用手动序列化的方式，换句话说，就是不借助默认的序列化器，而是我们自己来控制序列化的动作，同时，我们只采用String的序列化器，这样，在存储value时，我们就不需要在内存中就不用多存储数据，从而节约我们的内存空间</p>
<p><img src="/.%5CRedis.assets%5C1653054744832.png" class="lazyload" data-srcset="/.%5CRedis.assets%5C1653054744832.png" srcset="" alt="1653054744832"></p>
<p>这种用法比较普遍，因此SpringDataRedis就提供了RedisTemplate的子类：StringRedisTemplate，它的key和value的序列化方式默认就是String方式。</p>
<p><img src="https://i.imgur.com/zXH6Qn6.png" class="lazyload" data-srcset="https://i.imgur.com/zXH6Qn6.png" srcset=""></p>
<p>省去了我们自定义RedisTemplate的序列化方式的步骤，而是直接使用：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@SpringBootTest</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">RedisStringTests</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> StringRedisTemplate stringRedisTemplate;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">testString</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="comment">// 写入一条String数据</span></span><br><span class="line">        stringRedisTemplate.opsForValue().set(<span class="string">&quot;verify:phone:13600527634&quot;</span>, <span class="string">&quot;124143&quot;</span>);</span><br><span class="line">        <span class="comment">// 获取string数据</span></span><br><span class="line">        <span class="type">Object</span> <span class="variable">name</span> <span class="operator">=</span> stringRedisTemplate.opsForValue().get(<span class="string">&quot;name&quot;</span>);</span><br><span class="line">        System.out.println(<span class="string">&quot;name = &quot;</span> + name);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">ObjectMapper</span> <span class="variable">mapper</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ObjectMapper</span>();</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">testSaveUser</span><span class="params">()</span> <span class="keyword">throws</span> JsonProcessingException &#123;</span><br><span class="line">        <span class="comment">// 创建对象</span></span><br><span class="line">        <span class="type">User</span> <span class="variable">user</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">User</span>(<span class="string">&quot;虎哥&quot;</span>, <span class="number">21</span>);</span><br><span class="line">        <span class="comment">// 手动序列化</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">json</span> <span class="operator">=</span> mapper.writeValueAsString(user);</span><br><span class="line">        <span class="comment">// 写入数据</span></span><br><span class="line">        stringRedisTemplate.opsForValue().set(<span class="string">&quot;user:200&quot;</span>, json);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 获取数据</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">jsonUser</span> <span class="operator">=</span> stringRedisTemplate.opsForValue().get(<span class="string">&quot;user:200&quot;</span>);</span><br><span class="line">        <span class="comment">// 手动反序列化</span></span><br><span class="line">        <span class="type">User</span> <span class="variable">user1</span> <span class="operator">=</span> mapper.readValue(jsonUser, User.class);</span><br><span class="line">        System.out.println(<span class="string">&quot;user1 = &quot;</span> + user1);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>此时我们再来看一看存储的数据，小伙伴们就会发现那个class数据已经不在了，节约了我们的空间~</p>
<p><img src="/.%5CRedis.assets%5C1653054945211.png" class="lazyload" data-srcset="/.%5CRedis.assets%5C1653054945211.png" srcset="" alt="1653054945211"></p>
<p>最后小总结：</p>
<p>RedisTemplate的两种序列化实践方案：</p>
<ul>
<li><p>方案一：</p>
<ul>
<li>自定义RedisTemplate</li>
<li>修改RedisTemplate的序列化器为GenericJackson2JsonRedisSerializer</li>
</ul>
</li>
<li><p>方案二：</p>
<ul>
<li>使用StringRedisTemplate</li>
<li>写入Redis时，手动把对象序列化为JSON</li>
<li>读取Redis时，手动把读取到的JSON反序列化为对象</li>
</ul>
</li>
</ul>
<h3 id="6-4-Hash结构操作"><a href="#6-4-Hash结构操作" class="headerlink" title="6.4 Hash结构操作"></a>6.4 Hash结构操作</h3><p>在基础篇的最后，咱们对Hash结构操作一下，收一个小尾巴，这个代码咱们就不再解释啦</p>
<p>马上就开始新的篇章~~~进入到我们的Redis实战篇</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@SpringBootTest</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">RedisStringTests</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> StringRedisTemplate stringRedisTemplate;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">testHash</span><span class="params">()</span> &#123;</span><br><span class="line">        stringRedisTemplate.opsForHash().put(<span class="string">&quot;user:400&quot;</span>, <span class="string">&quot;name&quot;</span>, <span class="string">&quot;虎哥&quot;</span>);</span><br><span class="line">        stringRedisTemplate.opsForHash().put(<span class="string">&quot;user:400&quot;</span>, <span class="string">&quot;age&quot;</span>, <span class="string">&quot;21&quot;</span>);</span><br><span class="line"></span><br><span class="line">        Map&lt;Object, Object&gt; entries = stringRedisTemplate.opsForHash().entries(<span class="string">&quot;user:400&quot;</span>);</span><br><span class="line">        System.out.println(<span class="string">&quot;entries = &quot;</span> + entries);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>







<h3 id="Redis的应用场景"><a href="#Redis的应用场景" class="headerlink" title="Redis的应用场景"></a>Redis的应用场景</h3><h4 id="2-1-取最新N个数据的操作"><a href="#2-1-取最新N个数据的操作" class="headerlink" title="#2.1 取最新N个数据的操作"></a><a target="_blank" rel="noopener" href="https://ydlclass.com/doc21xnv/java/third/framework/4%E3%80%81redis/#_2-1-%E5%8F%96%E6%9C%80%E6%96%B0n%E4%B8%AA%E6%95%B0%E6%8D%AE%E7%9A%84%E6%93%8D%E4%BD%9C">#</a>2.1 取最新N个数据的操作</h4><p>比如典型的取网站最新文章，可以将最新的5000条评论ID放在Redis的List集合中，并将超出集合部分从数据库获取</p>
<h4 id="2-2-排行榜应用，取TOP-N操作"><a href="#2-2-排行榜应用，取TOP-N操作" class="headerlink" title="#2.2.排行榜应用，取TOP N操作"></a><a target="_blank" rel="noopener" href="https://ydlclass.com/doc21xnv/java/third/framework/4%E3%80%81redis/#_2-2-%E6%8E%92%E8%A1%8C%E6%A6%9C%E5%BA%94%E7%94%A8-%E5%8F%96top-n%E6%93%8D%E4%BD%9C">#</a>2.2.排行榜应用，取TOP N操作</h4><p>这个需求与上面需求的不同之处在于，前面操作以时间为权重，这个是以某个条件为权重，比如按顶的次数排序，可以使用Redis的sorted set，将要排序的值设置成sorted set的score，将具体的数据设置成相应的value，每次只需要执行一条ZADD命令即可。</p>
<h4 id="2-3需要精准设定过期时间的应用"><a href="#2-3需要精准设定过期时间的应用" class="headerlink" title="#2.3需要精准设定过期时间的应用"></a><a target="_blank" rel="noopener" href="https://ydlclass.com/doc21xnv/java/third/framework/4%E3%80%81redis/#_2-3%E9%9C%80%E8%A6%81%E7%B2%BE%E5%87%86%E8%AE%BE%E5%AE%9A%E8%BF%87%E6%9C%9F%E6%97%B6%E9%97%B4%E7%9A%84%E5%BA%94%E7%94%A8">#</a>2.3需要精准设定过期时间的应用</h4><p>比如可以把上面说到的sorted set的score值设置成过期时间的时间戳，那么就可以简单地通过过期时间排序，定时清除过期数据了，不仅是清除Redis中的过期数据，你完全可以把Redis里这个过期时间当成是对数据库中数据的索引，用Redis来找出哪些数据需要过期删除，然后再精准地从数据库中删除相应的记录。</p>
<h4 id="2-4计数器应用"><a href="#2-4计数器应用" class="headerlink" title="#2.4计数器应用"></a><a target="_blank" rel="noopener" href="https://ydlclass.com/doc21xnv/java/third/framework/4%E3%80%81redis/#_2-4%E8%AE%A1%E6%95%B0%E5%99%A8%E5%BA%94%E7%94%A8">#</a>2.4计数器应用</h4><p>Redis的命令都是原子性的，你可以轻松地利用INCR，DECR命令来构建计数器系统。</p>
<h4 id="2-5Uniq操作，获取某段时间所有数据排重值"><a href="#2-5Uniq操作，获取某段时间所有数据排重值" class="headerlink" title="#2.5Uniq操作，获取某段时间所有数据排重值"></a><a target="_blank" rel="noopener" href="https://ydlclass.com/doc21xnv/java/third/framework/4%E3%80%81redis/#_2-5uniq%E6%93%8D%E4%BD%9C-%E8%8E%B7%E5%8F%96%E6%9F%90%E6%AE%B5%E6%97%B6%E9%97%B4%E6%89%80%E6%9C%89%E6%95%B0%E6%8D%AE%E6%8E%92%E9%87%8D%E5%80%BC">#</a>2.5Uniq操作，获取某段时间所有数据排重值</h4><p>这个使用Redis的set数据结构最合适了，只需要不断地将数据往set中扔就行了，set意为集合，所以会自动排重。</p>
<h4 id="2-6实时系统，反垃圾系统"><a href="#2-6实时系统，反垃圾系统" class="headerlink" title="#2.6实时系统，反垃圾系统"></a><a target="_blank" rel="noopener" href="https://ydlclass.com/doc21xnv/java/third/framework/4%E3%80%81redis/#_2-6%E5%AE%9E%E6%97%B6%E7%B3%BB%E7%BB%9F-%E5%8F%8D%E5%9E%83%E5%9C%BE%E7%B3%BB%E7%BB%9F">#</a>2.6实时系统，反垃圾系统</h4><p>通过上面说到的set功能，你可以知道一个终端用户是否进行了某个操作，可以找到其操作的集合并进行分析统计对比等。没有做不到，只有想不到。</p>
<h4 id="2-7缓存"><a href="#2-7缓存" class="headerlink" title="#2.7缓存"></a><a target="_blank" rel="noopener" href="https://ydlclass.com/doc21xnv/java/third/framework/4%E3%80%81redis/#_2-7%E7%BC%93%E5%AD%98">#</a>2.7缓存</h4><p>将数据直接存放到内存中，性能优于Memcached，数据结构更多样化。</p>
<h1 id="缓存"><a href="#缓存" class="headerlink" title="缓存"></a>缓存</h1><h2 id="缓存的介绍"><a href="#缓存的介绍" class="headerlink" title="缓存的介绍"></a>缓存的介绍</h2><p>假如有一个接口，是通过id查询用户的信息</p>
<p>查询的时候 ，直接走的数据库去查询 </p>
<p><img src="F:\JAVA笔记库\img\id.png" class="lazyload" data-srcset="F:\JAVA笔记库\img\id.png" srcset=""></p>
<p>这时候就可以添加一个redis缓存，提高查询效率，减轻数据库压力</p>
<h2 id="缓存的业务流程"><a href="#缓存的业务流程" class="headerlink" title="缓存的业务流程:"></a>缓存的业务流程:</h2><h6 id="流程"><a href="#流程" class="headerlink" title="流程"></a>流程</h6><p>首先，客户端给出一个id，服务器接受请求后，根据id查询的时候会先查询redis中是否有与id一样的缓存</p>
<p>如果有 那么就是命中了，直接返回缓存信息就可以</p>
<p>如果没有命中，那么我们就需要去查询数据库，然后，查询的信息先存入redis，以便于下次使用，然后返回客户端</p>
<h6 id="伪代码"><a href="#伪代码" class="headerlink" title="伪代码:"></a>伪代码:</h6><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//请求</span></span><br><span class="line"><span class="meta">@GetMapping(&quot;/user/&#123;id&#125;&quot;)</span></span><br><span class="line"><span class="keyword">public</span> Result <span class="title function_">getById</span><span class="params">(<span class="meta">@PathVarible</span> Integer id)</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">//service</span></span><br><span class="line"><span class="keyword">public</span> Result <span class="title function_">getById</span><span class="params">(Integer id)</span>&#123;</span><br><span class="line">    <span class="type">StringRedisTemplate</span> <span class="variable">redistemplate</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">StringRedisTemplate</span>();</span><br><span class="line">    <span class="comment">//首先根据id查询redis</span></span><br><span class="line">    List&lt;User&gt; list = redisTemplate.opsForValue().get(id);</span><br><span class="line">    <span class="comment">//判断是否存在缓存</span></span><br><span class="line">    <span class="keyword">if</span>(list == <span class="literal">null</span> || list.size() == <span class="number">0</span>)&#123;</span><br><span class="line">        <span class="comment">//查询数据库</span></span><br><span class="line">        List&lt;User&gt; list = mapper.select(id);</span><br><span class="line">        <span class="comment">//设置缓存</span></span><br><span class="line">        redisTemplate.opsForValue().set(id,list)</span><br><span class="line">        <span class="comment">//返回结果</span></span><br><span class="line">        <span class="keyword">return</span> OK;</span><br><span class="line">    &#125;<span class="keyword">else</span>&#123;</span><br><span class="line">        <span class="comment">//缓存里有</span></span><br><span class="line">        <span class="keyword">return</span> OK;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">false</span> <span class="comment">//没有查询到</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h6 id="结构图"><a href="#结构图" class="headerlink" title="结构图"></a>结构图</h6><p><img src="F:\JAVA笔记库\img\rediss.png" class="lazyload" data-srcset="F:\JAVA笔记库\img\rediss.png" srcset=""></p>
<h2 id="缓存的问题"><a href="#缓存的问题" class="headerlink" title="缓存的问题"></a>缓存的问题</h2><h6 id="缓存存在的问题"><a href="#缓存存在的问题" class="headerlink" title="缓存存在的问题"></a>缓存存在的问题</h6><p>如果我们存到缓存里的数据就旧的，而数据库查询的是新的，用户就会拿到错误数据</p>
<p>TTL就是缓存过期时间 也就是expire()方法</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">stringRedisTemplate.expire()</span><br></pre></td></tr></table></figure>



<h6 id="根据变化频率选择跟新策略"><a href="#根据变化频率选择跟新策略" class="headerlink" title="根据变化频率选择跟新策略"></a>根据变化频率选择跟新策略</h6><p><img src="F:\JAVA笔记库\img\redisgengxin.png" class="lazyload" data-srcset="F:\JAVA笔记库\img\redisgengxin.png" srcset=""></p>
<p><img src="F:\JAVA笔记库\img\redis更新.png" class="lazyload" data-srcset="F:\JAVA笔记库\img\redis更新.png" srcset=""></p>
<h6 id="缓存实现的方式"><a href="#缓存实现的方式" class="headerlink" title="缓存实现的方式:"></a>缓存实现的方式:</h6><p>1.更新数据库同时更新redis 2.把缓存和数据库弄成一个微服务 3.新开一个线程专门操作redis</p>
<p><img src="F:\JAVA笔记库\img\xiancheng.png" class="lazyload" data-srcset="F:\JAVA笔记库\img\xiancheng.png" srcset=""></p>
<h6 id="最佳解决："><a href="#最佳解决：" class="headerlink" title="最佳解决："></a>最佳解决：</h6><p><img src="F:\JAVA笔记库\img\reddd.png" class="lazyload" data-srcset="F:\JAVA笔记库\img\reddd.png" srcset=""></p>
<h2 id="缓存穿透"><a href="#缓存穿透" class="headerlink" title="缓存穿透"></a>缓存穿透</h2><h5 id="概念"><a href="#概念" class="headerlink" title="概念"></a>概念</h5><p>缓存穿透就是当我们请求一个不存在的id希望返回结果时候，redis中并没有这个缓存，只能去查数据库，然后数据库查询后发现并没有，返回一个null，这时候，高并发的去查询这个不存在的id，redis这时候就没用了</p>
<h5 id="解决方案"><a href="#解决方案" class="headerlink" title="解决方案"></a>解决方案</h5><p>1.把空对象，也就是数据库查询的null，缓存到redis，这样就不会出现穿透问题,</p>
<p>但是这样会让redis中有很多垃圾数据，我们也可以设置ttl，来保证内存不要崩,而且可能造成短期不一致，比如我们缓存了空对象，但是我们真的插入了一个新的数据，用户直接查询了缓存 ，就不一致了</p>
<p><img src="F:\JAVA笔记库\img\ttl.png" class="lazyload" data-srcset="F:\JAVA笔记库\img\ttl.png" srcset=""></p>
<p>2.布隆过滤器</p>
<p><img src="F:\JAVA笔记库\img\布隆.png" class="lazyload" data-srcset="F:\JAVA笔记库\img\布隆.png" srcset=""></p>
<h2 id="缓存雪崩"><a href="#缓存雪崩" class="headerlink" title="缓存雪崩"></a>缓存雪崩</h2><h3 id="概念-1"><a href="#概念-1" class="headerlink" title="概念"></a>概念</h3><p>一瞬间几十万的key都失效了，就会一瞬间访问数据库导致服务器宕机，还有一种可能就是redis直接宕机，所有的缓存全部失效，所有请求访问数据库，直接完蛋</p>
<h3 id="解决方式"><a href="#解决方式" class="headerlink" title="解决方式"></a>解决方式</h3><ul>
<li>可以给不同的key添加TTL随机值</li>
<li>利用redis集群 哨兵机制</li>
<li>业务添加多级缓存</li>
</ul>
<h2 id="缓存击穿"><a href="#缓存击穿" class="headerlink" title="缓存击穿"></a>缓存击穿</h2><p>这是由于部分key的过期问题 无数的线程来访问的时候未命中缓存</p>
<p><img src="F:\JAVA笔记库\img\redis1.png" class="lazyload" data-srcset="F:\JAVA笔记库\img\redis1.png" srcset=""></p>
<p>图解</p>
<p><img src="F:\JAVA笔记库\img\redis2.png" class="lazyload" data-srcset="F:\JAVA笔记库\img\redis2.png" srcset=""></p>
<p>解决方式</p>
<p><img src="F:\JAVA笔记库\img\redis3.png" class="lazyload" data-srcset="F:\JAVA笔记库\img\redis3.png" srcset=""></p>
<h3 id="利用互斥锁方式来解决"><a href="#利用互斥锁方式来解决" class="headerlink" title="利用互斥锁方式来解决"></a>利用互斥锁方式来解决</h3><p>我们可以利用redis中的setnx的互斥的特性来实现互斥锁的实现 因为<code>setnx</code>命令只有在键不存在的时候才会创建这么一个key，否则无法创建 当我们释放锁的时候可以直接删除掉键就可以</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">del lock </span><br></pre></td></tr></table></figure>



<p><img src="F:\JAVA笔记库\img\redis4.png" class="lazyload" data-srcset="F:\JAVA笔记库\img\redis4.png" srcset=""></p>
<p><code>setnx</code>的命令详解</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">setnx lock 1		//只有这个lock这个键不存在的时候才会设置</span><br><span class="line"></span><br><span class="line">//往往会设置一个时间 比如一秒钟</span><br><span class="line">redisTemplate.expire(&quot;lock&quot;,1min );</span><br></pre></td></tr></table></figure>

<p><img src="F:\JAVA笔记库\img\redis5.png" class="lazyload" data-srcset="F:\JAVA笔记库\img\redis5.png" srcset=""></p>
<p>业务逻辑代码:</p>
<p>获取锁和删除锁的逻辑</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">* 获取锁</span><br><span class="line">   *</span><br><span class="line">   * <span class="meta">@param</span> key 关键</span><br><span class="line">   * <span class="meta">@return</span> <span class="type">boolean</span></span><br><span class="line">   *<span class="comment">//*</span></span><br><span class="line">  <span class="keyword">private</span> <span class="type">boolean</span> <span class="title function_">tryLock</span><span class="params">(String key)</span> &#123;</span><br><span class="line">      <span class="type">Boolean</span> <span class="variable">flag</span> <span class="operator">=</span> stringRedisTemplate.opsForValue().setIfAbsent(key, <span class="string">&quot;1&quot;</span>, LOCK_SHOP_TTL, 									TimeUnit.SECONDS);</span><br><span class="line">      <span class="keyword">return</span> BooleanUtil.isTrue(flag);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  *<span class="comment">//**</span></span><br><span class="line">   * 释放锁</span><br><span class="line">   *</span><br><span class="line">   * <span class="meta">@param</span> key 关键</span><br><span class="line">   *<span class="comment">//*</span></span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">unLock</span><span class="params">(String key)</span> &#123;</span><br><span class="line">      stringRedisTemplate.delete(key);</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure>

<p>业务 </p>
<p>逻辑:</p>
<p><img src="F:\JAVA笔记库\img\redis6.png" class="lazyload" data-srcset="F:\JAVA笔记库\img\redis6.png" srcset=""></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">    * 互斥锁解决缓存击穿</span></span><br><span class="line"><span class="comment">    *</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@param</span> id id</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@return</span> &#123;<span class="doctag">@link</span> Shop&#125;</span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">   <span class="keyword">private</span> Shop <span class="title function_">queryWithMutex</span><span class="params">(Long id)</span> &#123;</span><br><span class="line">       <span class="type">String</span> <span class="variable">shopKey</span> <span class="operator">=</span> CACHE_SHOP_KEY + id;</span><br><span class="line">       <span class="comment">//从redis中查询</span></span><br><span class="line">       <span class="type">String</span> <span class="variable">shopJson</span> <span class="operator">=</span> stringRedisTemplate.opsForValue().get(shopKey);</span><br><span class="line">       <span class="comment">//判断是否存在</span></span><br><span class="line">       <span class="keyword">if</span> (StringUtils.isNotEmpty(shopJson)) &#123;</span><br><span class="line">           <span class="comment">//存在直接返回</span></span><br><span class="line">           <span class="keyword">return</span> JSONUtil.toBean(shopJson, Shop.class);</span><br><span class="line">       &#125;</span><br><span class="line">       <span class="comment">//判断控值</span></span><br><span class="line">       <span class="keyword">if</span> (<span class="string">&quot;&quot;</span>.equals(shopJson)) &#123;</span><br><span class="line">           <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">       &#125;</span><br><span class="line">       <span class="comment">//实现缓存重建</span></span><br><span class="line">       <span class="comment">//获取互斥锁</span></span><br><span class="line">       <span class="type">String</span> <span class="variable">lockKey</span> <span class="operator">=</span> LOCK_SHOP_KEY + id;</span><br><span class="line">       <span class="type">Shop</span> <span class="variable">shop</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">       <span class="keyword">try</span> &#123;</span><br><span class="line">           <span class="type">boolean</span> <span class="variable">isLock</span> <span class="operator">=</span> tryLock(lockKey);</span><br><span class="line">           <span class="comment">//是否获取成功</span></span><br><span class="line">           <span class="keyword">if</span> (!isLock) &#123;</span><br><span class="line">               <span class="comment">//获取失败 休眠并且重试</span></span><br><span class="line">               Thread.sleep(<span class="number">50</span>);</span><br><span class="line">               <span class="keyword">return</span> queryWithMutex(id);</span><br><span class="line">           &#125;</span><br><span class="line">           <span class="comment">//成功 通过id查询数据库</span></span><br><span class="line">           shop = getById(id);</span><br><span class="line">           <span class="comment">//模拟重建延时</span></span><br><span class="line">           Thread.sleep(<span class="number">200</span>);</span><br><span class="line">           <span class="keyword">if</span> (shop == <span class="literal">null</span>) &#123;</span><br><span class="line">               <span class="comment">//redis写入空值</span></span><br><span class="line">               stringRedisTemplate.opsForValue().set(shopKey, <span class="string">&quot;&quot;</span>, CACHE_NULL_TTL, 								TimeUnit.MINUTES);</span><br><span class="line">               <span class="comment">//数据库不存在 返回错误</span></span><br><span class="line">               <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">           &#125;</span><br><span class="line">           <span class="comment">//数据库存在 写入redis</span></span><br><span class="line">           stringRedisTemplate.opsForValue().set(shopKey, JSONUtil.toJsonStr(shop), 							CACHE_SHOP_TTL, TimeUnit.MINUTES);</span><br><span class="line">       &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">           <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(e);</span><br><span class="line">       &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">           <span class="comment">//释放互斥锁</span></span><br><span class="line">           unLock(lockKey);</span><br><span class="line">       &#125;</span><br><span class="line">       <span class="comment">//返回</span></span><br><span class="line">       <span class="keyword">return</span> shop;</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure>


  </div>
  
  
    
    <div class='footer'>
       <!-- 参考资料、相关资料等 -->
      
       <!-- 相关文章 -->
      
      <!-- 版权声明组件 -->
      
      <!-- 打赏组件 -->
      
    </div>
  
  
    


  <div class='article-meta' id="bottom">
    <div class='new-meta-box'>
      
        
          <div class="new-meta-item date" itemprop="dateModified" datetime="2024-01-25T15:35:02+08:00">
  <a class='notlink'>
    <i class="fa-solid fa-edit fa-fw" aria-hidden="true"></i>
    <p>更新于：Jan 25, 2024</p>
  </a>
</div>

        
      
        
          

        
      
        
          
  <div class="new-meta-item share -mob-share-list">
  <div class="-mob-share-list share-body">
    
      
        <a class="-mob-share-qq" title="" rel="external nofollow noopener noreferrer noopener"
          
          target="_blank" href="http://connect.qq.com/widget/shareqq/index.html?url=http://8.219.11.28/2024/02/23/%E5%B7%A5%E5%85%B7-Redis/&title= - 欢迎来到我的个人博客~&summary="
          
          >
          
            <img src="https://unpkg.com/volantis-static@0.0.1654736714924/media/org.volantis/logo/128/qq.png" class="lazyload" data-srcset="https://unpkg.com/volantis-static@0.0.1654736714924/media/org.volantis/logo/128/qq.png" srcset="">
          
        </a>
      
    
      
        <a class="-mob-share-qzone" title="" rel="external nofollow noopener noreferrer noopener"
          
          target="_blank" href="https://sns.qzone.qq.com/cgi-bin/qzshare/cgi_qzshare_onekey?url=http://8.219.11.28/2024/02/23/%E5%B7%A5%E5%85%B7-Redis/&title= - 欢迎来到我的个人博客~&summary="
          
          >
          
            <img src="https://unpkg.com/volantis-static@0.0.1654736714924/media/org.volantis/logo/128/qzone.png" class="lazyload" data-srcset="https://unpkg.com/volantis-static@0.0.1654736714924/media/org.volantis/logo/128/qzone.png" srcset="">
          
        </a>
      
    
      
        <a class="-mob-share-weibo" title="" rel="external nofollow noopener noreferrer noopener"
          
          target="_blank" href="http://service.weibo.com/share/share.php?url=http://8.219.11.28/2024/02/23/%E5%B7%A5%E5%85%B7-Redis/&title= - 欢迎来到我的个人博客~&summary="
          
          >
          
            <img src="https://unpkg.com/volantis-static@0.0.1654736714924/media/org.volantis/logo/128/weibo.png" class="lazyload" data-srcset="https://unpkg.com/volantis-static@0.0.1654736714924/media/org.volantis/logo/128/weibo.png" srcset="">
          
        </a>
      
    
      
    
      
    
  </div>
</div>



        
      
    </div>
    <!-- Custom Files bottomMeta begin -->
    
    <!-- Custom Files bottomMeta end -->
  </div>


  
  

  
    <div class="prev-next">
      
        <a class='prev' href='/2024/02/23/%E5%B7%A5%E5%85%B7-SSH%E5%91%BD%E4%BB%A4/'>
          <p class='title'><i class="fa-solid fa-chevron-left" aria-hidden="true"></i>Feb 23, 2024</p>
          <p class='content'>SSH命令
[通过 SSH 在远程和本地系统之间传输文件的 4 种方法 - 知乎 (zhihu.com)](https://zhuanlan.zhihu.com/p/507876254#:~:t...</p>
        </a>
      
      
        <a class='next' href='/2024/02/23/%E5%B7%A5%E5%85%B7-Git%20%E5%9F%BA%E6%9C%AC%E4%BD%BF%E7%94%A8/'>
          <p class='title'>Feb 23, 2024<i class="fa-solid fa-chevron-right" aria-hidden="true"></i></p>
          <p class='content'>Git基本使用使用过程首先，我们在需要被托管的文件中，打开cmd窗口，构建一个本地仓库

然后就会生成一个.git文件，这个文件在哪里，就说明我们要托管哪一个文件

然后，我们创建一个远程的仓库...</p>
        </a>
      
    </div>
  
  <!-- Custom Files postEnd begin-->
  
  <!-- Custom Files postEnd end-->
</article>


  


  <article class="post white-box shadow floatable blur" id="comments">
    <span hidden>
      <meta itemprop="discussionUrl" content="/2024/02/23/%E5%B7%A5%E5%85%B7-Redis/index.html#comments">
    </span>
    <p ct><i class='fa-solid fa-comments'></i> 评论</p>
    

    <div id="layoutHelper-comments"></div>

  </article>






</div>
<aside id='l_side' itemscope itemtype="http://schema.org/WPSideBar">
  

  
    
    
      
    
  


<div class="widget-sticky pjax">

  
  


  <section class="widget toc-wrapper desktop mobile " id="toc-div" >
    
  <header>
    
      <i class="fa-solid fa-list fa-fw" aria-hidden="true"></i><span class='name'>本文目录</span>
    
  </header>


    <div class='content'>
        <ol class="toc"><li class="toc-item toc-level-2"><a class="toc-link" href="#1-Redis%E7%AE%80%E5%8D%95%E4%BB%8B%E7%BB%8D"><span class="toc-text">1.Redis简单介绍</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#2-%E8%AF%BE%E7%A8%8B%E7%9B%AE%E5%BD%95"><span class="toc-text">2.课程目录</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#3-%E5%88%9D%E5%A7%8BRedis"><span class="toc-text">3.初始Redis</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#3-1-%E8%AE%A4%E8%AF%86NoSQL"><span class="toc-text">3.1.认识NoSQL</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#3-1-1-%E7%BB%93%E6%9E%84%E5%8C%96%E4%B8%8E%E9%9D%9E%E7%BB%93%E6%9E%84%E5%8C%96"><span class="toc-text">3.1.1.结构化与非结构化</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#3-1-2-%E5%85%B3%E8%81%94%E5%92%8C%E9%9D%9E%E5%85%B3%E8%81%94"><span class="toc-text">3.1.2.关联和非关联</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#3-1-3-%E6%9F%A5%E8%AF%A2%E6%96%B9%E5%BC%8F"><span class="toc-text">3.1.3.查询方式</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#3-1-4-%E4%BA%8B%E5%8A%A1"><span class="toc-text">3.1.4.事务</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#3-1-5-%E6%80%BB%E7%BB%93"><span class="toc-text">3.1.5.总结</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#3-2-%E8%AE%A4%E8%AF%86Redis"><span class="toc-text">3.2.认识Redis</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#3-3-%E5%AE%89%E8%A3%85Redis"><span class="toc-text">3.3.安装Redis</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#3-3-1-%E4%BE%9D%E8%B5%96%E5%BA%93"><span class="toc-text">3.3.1.依赖库</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#3-3-2-%E4%B8%8A%E4%BC%A0%E5%AE%89%E8%A3%85%E5%8C%85%E5%B9%B6%E8%A7%A3%E5%8E%8B"><span class="toc-text">3.3.2.上传安装包并解压</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#3-3-3-%E5%90%AF%E5%8A%A8"><span class="toc-text">3.3.3.启动</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#3-3-4-%E9%BB%98%E8%AE%A4%E5%90%AF%E5%8A%A8"><span class="toc-text">3.3.4.默认启动</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#3-3-5-%E6%8C%87%E5%AE%9A%E9%85%8D%E7%BD%AE%E5%90%AF%E5%8A%A8"><span class="toc-text">3.3.5.指定配置启动</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#3-3-6-%E5%BC%80%E6%9C%BA%E8%87%AA%E5%90%AF"><span class="toc-text">3.3.6.开机自启</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#3-4-Redis%E6%A1%8C%E9%9D%A2%E5%AE%A2%E6%88%B7%E7%AB%AF"><span class="toc-text">3.4.Redis桌面客户端</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#3-4-1-Redis%E5%91%BD%E4%BB%A4%E8%A1%8C%E5%AE%A2%E6%88%B7%E7%AB%AF"><span class="toc-text">3.4.1.Redis命令行客户端</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#3-4-2-%E5%9B%BE%E5%BD%A2%E5%8C%96%E6%A1%8C%E9%9D%A2%E5%AE%A2%E6%88%B7%E7%AB%AF"><span class="toc-text">3.4.2.图形化桌面客户端</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#3-4-3-%E5%AE%89%E8%A3%85"><span class="toc-text">3.4.3.安装</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#3-4-4-%E5%BB%BA%E7%AB%8B%E8%BF%9E%E6%8E%A5"><span class="toc-text">3.4.4.建立连接</span></a></li></ol></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#4-Redis%E5%B8%B8%E8%A7%81%E5%91%BD%E4%BB%A4"><span class="toc-text">4.Redis常见命令</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#4-1-Redis%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%BB%8B%E7%BB%8D"><span class="toc-text">4.1 Redis数据结构介绍</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#4-2-Redis-%E9%80%9A%E7%94%A8%E5%91%BD%E4%BB%A4"><span class="toc-text">4.2 Redis 通用命令</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#4-3-Redis%E5%91%BD%E4%BB%A4-String%E5%91%BD%E4%BB%A4"><span class="toc-text">4.3 Redis命令-String命令</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#4-4-Redis%E5%91%BD%E4%BB%A4-Key%E7%9A%84%E5%B1%82%E7%BA%A7%E7%BB%93%E6%9E%84"><span class="toc-text">4.4 Redis命令-Key的层级结构</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#4-5-Redis%E5%91%BD%E4%BB%A4-Hash%E5%91%BD%E4%BB%A4"><span class="toc-text">4.5 Redis命令-Hash命令</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#4-6-Redis%E5%91%BD%E4%BB%A4-List%E5%91%BD%E4%BB%A4"><span class="toc-text">4.6 Redis命令-List命令</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#4-7-Redis%E5%91%BD%E4%BB%A4-Set%E5%91%BD%E4%BB%A4"><span class="toc-text">4.7 Redis命令-Set命令</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#4-8-Redis%E5%91%BD%E4%BB%A4-SortedSet%E7%B1%BB%E5%9E%8B"><span class="toc-text">4.8 Redis命令-SortedSet类型</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#5-Redis%E7%9A%84Java%E5%AE%A2%E6%88%B7%E7%AB%AF-Jedis"><span class="toc-text">5.Redis的Java客户端-Jedis</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#5-1-Jedis%E5%BF%AB%E9%80%9F%E5%85%A5%E9%97%A8"><span class="toc-text">5.1 Jedis快速入门</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#5-2-Jedis%E8%BF%9E%E6%8E%A5%E6%B1%A0"><span class="toc-text">5.2 Jedis连接池</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#5-2-1-%E5%88%9B%E5%BB%BAJedis%E7%9A%84%E8%BF%9E%E6%8E%A5%E6%B1%A0"><span class="toc-text">5.2.1.创建Jedis的连接池</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#5-2-2-%E6%94%B9%E9%80%A0%E5%8E%9F%E5%A7%8B%E4%BB%A3%E7%A0%81"><span class="toc-text">5.2.2.改造原始代码</span></a></li></ol></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#6-Redis%E7%9A%84Java%E5%AE%A2%E6%88%B7%E7%AB%AF-SpringDataRedis"><span class="toc-text">6.Redis的Java客户端-SpringDataRedis</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#6-1-%E5%BF%AB%E9%80%9F%E5%85%A5%E9%97%A8"><span class="toc-text">6.1.快速入门</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#6-1-1-%E5%AF%BC%E5%85%A5pom%E5%9D%90%E6%A0%87"><span class="toc-text">6.1.1.导入pom坐标</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#6-1-2-%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6"><span class="toc-text">6.1.2 .配置文件</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#6-1-3-%E6%B5%8B%E8%AF%95%E4%BB%A3%E7%A0%81"><span class="toc-text">6.1.3.测试代码</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#6-2-%E6%95%B0%E6%8D%AE%E5%BA%8F%E5%88%97%E5%8C%96%E5%99%A8"><span class="toc-text">6.2 .数据序列化器</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#6-3-StringRedisTemplate"><span class="toc-text">6.3 StringRedisTemplate</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#6-4-Hash%E7%BB%93%E6%9E%84%E6%93%8D%E4%BD%9C"><span class="toc-text">6.4 Hash结构操作</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Redis%E7%9A%84%E5%BA%94%E7%94%A8%E5%9C%BA%E6%99%AF"><span class="toc-text">Redis的应用场景</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#2-1-%E5%8F%96%E6%9C%80%E6%96%B0N%E4%B8%AA%E6%95%B0%E6%8D%AE%E7%9A%84%E6%93%8D%E4%BD%9C"><span class="toc-text">2.1 取最新N个数据的操作</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#2-2-%E6%8E%92%E8%A1%8C%E6%A6%9C%E5%BA%94%E7%94%A8%EF%BC%8C%E5%8F%96TOP-N%E6%93%8D%E4%BD%9C"><span class="toc-text">2.2.排行榜应用，取TOP N操作</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#2-3%E9%9C%80%E8%A6%81%E7%B2%BE%E5%87%86%E8%AE%BE%E5%AE%9A%E8%BF%87%E6%9C%9F%E6%97%B6%E9%97%B4%E7%9A%84%E5%BA%94%E7%94%A8"><span class="toc-text">2.3需要精准设定过期时间的应用</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#2-4%E8%AE%A1%E6%95%B0%E5%99%A8%E5%BA%94%E7%94%A8"><span class="toc-text">2.4计数器应用</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#2-5Uniq%E6%93%8D%E4%BD%9C%EF%BC%8C%E8%8E%B7%E5%8F%96%E6%9F%90%E6%AE%B5%E6%97%B6%E9%97%B4%E6%89%80%E6%9C%89%E6%95%B0%E6%8D%AE%E6%8E%92%E9%87%8D%E5%80%BC"><span class="toc-text">2.5Uniq操作，获取某段时间所有数据排重值</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#2-6%E5%AE%9E%E6%97%B6%E7%B3%BB%E7%BB%9F%EF%BC%8C%E5%8F%8D%E5%9E%83%E5%9C%BE%E7%B3%BB%E7%BB%9F"><span class="toc-text">2.6实时系统，反垃圾系统</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#2-7%E7%BC%93%E5%AD%98"><span class="toc-text">2.7缓存</span></a></li></ol></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E7%BC%93%E5%AD%98%E7%9A%84%E4%BB%8B%E7%BB%8D"><span class="toc-text">缓存的介绍</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E7%BC%93%E5%AD%98%E7%9A%84%E4%B8%9A%E5%8A%A1%E6%B5%81%E7%A8%8B"><span class="toc-text">缓存的业务流程:</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E7%BC%93%E5%AD%98%E7%9A%84%E9%97%AE%E9%A2%98"><span class="toc-text">缓存的问题</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E7%BC%93%E5%AD%98%E7%A9%BF%E9%80%8F"><span class="toc-text">缓存穿透</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#%E6%A6%82%E5%BF%B5"><span class="toc-text">概念</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88"><span class="toc-text">解决方案</span></a></li></ol></li></ol></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E7%BC%93%E5%AD%98%E9%9B%AA%E5%B4%A9"><span class="toc-text">缓存雪崩</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%A6%82%E5%BF%B5-1"><span class="toc-text">概念</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E8%A7%A3%E5%86%B3%E6%96%B9%E5%BC%8F"><span class="toc-text">解决方式</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E7%BC%93%E5%AD%98%E5%87%BB%E7%A9%BF"><span class="toc-text">缓存击穿</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%88%A9%E7%94%A8%E4%BA%92%E6%96%A5%E9%94%81%E6%96%B9%E5%BC%8F%E6%9D%A5%E8%A7%A3%E5%86%B3"><span class="toc-text">利用互斥锁方式来解决</span></a></li></ol></li></ol>
    </div>
  </section>

  

</div>


<!-- 没有 pjax 占位会报错 万恶的 pjax -->

  <div class="pjax">
    <!-- pjax占位 -->
  </div>

  <div class="pjax">
    <!-- pjax占位 -->
  </div>

  <div class="pjax">
    <!-- pjax占位 -->
  </div>

  <div class="pjax">
    <!-- pjax占位 -->
  </div>

  <div class="pjax">
    <!-- pjax占位 -->
  </div>

  <div class="pjax">
    <!-- pjax占位 -->
  </div>

  <div class="pjax">
    <!-- pjax占位 -->
  </div>

  <div class="pjax">
    <!-- pjax占位 -->
  </div>

  <!-- Custom Files side begin -->
  
  <!-- Custom Files side end -->
</aside>



          <!--此文件用来存放一些不方便取值的变量-->
<!--思路大概是将值藏到重加载的区域内-->

<pjax>
<script>
  window.pdata={}
  pdata.ispage=false;
  pdata.commentPath="";
  pdata.commentPlaceholder="";
  pdata.commentConfig={};
  //  see: /layout/_partial/scripts/_ctrl/coverCtrl.ejs
  
    // header
    var l_header=document.getElementById("l_header");
    
    l_header.classList.add("show");
    
    
      // cover
      var cover_wrapper=document.querySelector('#l_cover .cover-wrapper');
      var scroll_down=document.getElementById('scroll-down');
      cover_wrapper.id="none";
      cover_wrapper.style.display="none";
      scroll_down.style.display="none";
    
  
</script>
</pjax>
        </div>
        
  
  <footer class="footer clearfix"  itemscope itemtype="http://schema.org/WPFooter">
    <br><br>
    
      
        <div class="aplayer-container">
          


        </div>
      
    
      
        <br>
        <div class="social-wrapper" itemprop="about" itemscope itemtype="http://schema.org/Thing">
          
            
          
            
          
            
          
        </div>
      
    
      
        <div><p>Blog content follows the <a target="_blank" rel="noopener" href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.en">Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0) License</a></p>
</div>
      
    
      
        
          <div><p><span id="lc-sv">本站总访问量为 <span id='number'><i class="fa-solid fa-loader fa-spin fa-fw" aria-hidden="true"></i></span> 次</span> <span id="lc-uv">访客数为 <span id='number'><i class="fa-solid fa-loader fa-spin fa-fw" aria-hidden="true"></i></span> 人</span></p>
</div>
        
      
    
      
        Use
        <a href="https://github.com/volantis-x/hexo-theme-volantis/#5.8.0" target="_blank" class="codename">Volantis</a>
        as theme
      
    
      
        <div class='copyright'>
        <p><a href="/">Copyright © since 2017 XXX</a></p>

        </div>
      
    
    <!-- Custom Files footer begin-->
    
    <!-- Custom Files footer end-->
  </footer>


        <a id="s-top" class="fa-solid fa-arrow-up fa-fw" href="/" onclick="return false;" title="top"></a>
      </div>
    </div>
    <div>
      <script>
  /******************** volantis.dom ********************************/
  // 页面选择器 将dom对象缓存起来 see: /source/js/app.js etc.
  volantis.dom.bodyAnchor = volantis.dom.$(document.getElementById("safearea")); // 页面主体
  volantis.dom.topBtn = volantis.dom.$(document.getElementById('s-top')); // 向上
  volantis.dom.wrapper = volantis.dom.$(document.getElementById('wrapper')); // 整个导航栏
  volantis.dom.coverAnchor = volantis.dom.$(document.querySelector('#l_cover .cover-wrapper')); // 1个
  volantis.dom.switcher = volantis.dom.$(document.querySelector('#l_header .switcher .s-search')); // 搜索按钮   移动端 1个
  volantis.dom.header = volantis.dom.$(document.getElementById('l_header')); // 移动端导航栏
  volantis.dom.search = volantis.dom.$(document.querySelector('#l_header .m_search')); // 搜索框 桌面端 移动端 1个
  volantis.dom.mPhoneList = volantis.dom.$(document.querySelectorAll('#l_header .m-phone .list-v')); //  手机端 子菜单 多个
</script>

<script>
  
  volantis.css("https://unpkg.com/volantis-static@0.0.1654736714924/libs/@fortawesome/fontawesome-free/css/all.min.css");
  
  
  
</script>

<!-- required -->


<!-- internal -->

<script src="/js/app.js"></script>






<!-- rightmenu要在darkmode之前（ToggleButton） darkmode要在comments之前（volantis.dark.push）-->



<script>
  function loadIssuesJS() {
    
      const sites_api = document.getElementById('sites-api');
      if (sites_api != undefined && typeof SitesJS === 'undefined') {
        volantis.js("/js/plugins/tags/sites.js")
      }
    
    
      const friends_api = document.getElementById('friends-api');
      if (friends_api != undefined && typeof FriendsJS === 'undefined') {
        volantis.js("/js/plugins/tags/friends.js")
      }
    
    
      const contributors_api = document.getElementById('contributors-api');
      if (contributors_api != undefined && typeof ContributorsJS === 'undefined') {
        volantis.js("/js/plugins/tags/contributors.js")
      }
    
  };
  loadIssuesJS()
  volantis.pjax.push(()=>{
    loadIssuesJS();
  })

</script>




  <script defer src="https://unpkg.com/volantis-static@0.0.1654736714924/libs/vanilla-lazyload/dist/lazyload.min.js"></script>
<script>
  // https://www.npmjs.com/package/vanilla-lazyload
  // Set the options globally
  // to make LazyLoad self-initialize
  window.lazyLoadOptions = {
    elements_selector: ".lazyload",
    threshold: 0
  };
  // Listen to the initialization event
  // and get the instance of LazyLoad
  window.addEventListener(
    "LazyLoad::Initialized",
    function (event) {
      window.lazyLoadInstance = event.detail.instance;
    },
    false
  );
  document.addEventListener('DOMContentLoaded', function () {
    lazyLoadInstance.update();
  });
  document.addEventListener('pjax:complete', function () {
    lazyLoadInstance.update();
  });
</script>




  

<script>
  window.FPConfig = {
	delay: 0,
	ignoreKeywords: ["#"],
	maxRPS: 6,
	hoverDelay: 0
  };
</script>
<script defer src="https://unpkg.com/volantis-static@0.0.1654736714924/libs/flying-pages/flying-pages.min.js"></script>









      <script>
  volantis.layoutHelper("comments",`<div id="giscus_container"></div>`)

  volantis.giscus = {};

  function check_giscus() {
    if (volantis.dark.mode === "dark") {
      volantis.giscus.Theme = 'dark';
    } else {
      volantis.giscus.Theme = 'light';
    }

    return document.getElementById("giscus_container");
  }

  function pjax_giscus() {
    const HEAD = check_giscus();
    if (!HEAD) return;
    let cfg = Object.assign({"theme":{"light":"light","dark":"dark"}},pdata.commentConfig)
    const script = document.createElement('script');
    script.setAttribute('src', 'https://giscus.app/client.js');
    Object.keys(cfg).forEach(k=>{
      if (k != "theme") {
        script.setAttribute('data-'+k, cfg[k]);
      }
    })
    script.setAttribute('data-theme', volantis.giscus.Theme);
    script.setAttribute('crossorigin', "anonymous");
    HEAD.appendChild(script);
  }

  function dark_giscus() {
    const HEAD = check_giscus();
    if (!HEAD) return;

    const message = {
      setConfig: {
        theme: volantis.giscus.Theme
      }
    };
    const giscusIframe = document.querySelector('iframe.giscus-frame');
    giscusIframe.contentWindow.postMessage({ giscus: message }, 'https://giscus.app');
  }
  pjax_giscus();
  volantis.pjax.push(pjax_giscus);
  volantis.dark.push(dark_giscus);
</script>

    





<!-- optional -->

  <script>
  const SearchServiceDataPathRoot = ("/" || "/").endsWith("/") ?
    "/" || "/" :
    "//" || "/";
  const SearchServiceDataPath = SearchServiceDataPathRoot + "content.json";

  function loadSearchScript() {
    // see: layout/_partial/scripts/_ctrl/cdnCtrl.ejs
    return volantis.js("/js/search/hexo.js");
  }

  function loadSearchService() {
    loadSearchScript();
    document.querySelectorAll(".input.u-search-input").forEach((e) => {
      e.removeEventListener("focus", loadSearchService, false);
    });

    document.querySelectorAll(".u-search-form").forEach((e) => {
      e.addEventListener("submit", (event) => {
        event.preventDefault();
      }, false);
    });
  }

  // 打开并搜索 字符串 s
  function OpenSearch(s) {
    if (typeof SearchService === 'undefined')
      loadSearchScript().then(() => {
        SearchService.setQueryText(s);
        SearchService.search();
      });
    else {
      SearchService.setQueryText(s);
      SearchService.search();
    }
  }

  // 访问含有 ?s=xxx  的链接时打开搜索 // 与搜索引擎 structured data 相关: /scripts/helpers/structured-data/lib/config.js
  if (window.location.search && /^\?s=/g.test(window.location.search)) {
    let queryText = decodeURI(window.location.search)
      .replace(/\ /g, "-")
      .replace(/^\?s=/g, "");
    OpenSearch(queryText);
  }

  // 搜索输入框获取焦点时加载搜索
  document.querySelectorAll(".input.u-search-input").forEach((e) => {
    e.addEventListener("focus", loadSearchService, false);
  });
</script>







  <script>



  function pjax_highlightjs_copyCode(){
    if (!(document.querySelector(".highlight .code pre") ||
      document.querySelector(".article pre code"))) {
      return;
    }
    VolantisApp.utilCopyCode(".highlight .code pre, .article pre code")
  }
  volantis.requestAnimationFrame(pjax_highlightjs_copyCode)
  volantis.pjax.push(pjax_highlightjs_copyCode)

</script>












  <script>
  function load_swiper() {
    if (!document.querySelectorAll(".swiper-container")[0]) return;
    volantis.css("https://unpkg.com/volantis-static@0.0.1654736714924/libs/swiper/swiper-bundle.min.css");
    volantis.js("https://unpkg.com/volantis-static@0.0.1654736714924/libs/swiper/swiper-bundle.min.js").then(() => {
      pjax_swiper();
    });
  }

  load_swiper();

  function pjax_swiper() {
    volantis.swiper = new Swiper('.swiper-container', {
      slidesPerView: 'auto',
      spaceBetween: 8,
      centeredSlides: true,
      loop: true,
      pagination: {
        el: '.swiper-pagination',
        clickable: true,
      },
      navigation: {
        nextEl: '.swiper-button-next',
        prevEl: '.swiper-button-prev',
      },
    });
  }

  volantis.pjax.push(() => {
    if (!document.querySelectorAll(".swiper-container")[0]) return;
    if (typeof volantis.swiper === "undefined") {
      load_swiper();
    } else {
      pjax_swiper();
    }
  });
</script>


<!-- pjax 标签必须存在于所有页面 否则 pjax error -->
<pjax>

</pjax>

<script>
  function listennSidebarTOC() {
    const navItems = document.querySelectorAll(".toc li");
    if (!navItems.length) return;
    let targets = []
    const sections = [...navItems].map((element) => {
      const link = element.querySelector(".toc-link");
      const target = document.getElementById(
        decodeURI(link.getAttribute("href")).replace("#", "")
      );
      targets.push(target)
      // 解除 a 标签 href 的 锚点定位, a 标签 href 的 锚点定位 会随机启用?? 产生错位???
      link.setAttribute("onclick","return false;")
      link.setAttribute("toc-action","toc-"+decodeURI(link.getAttribute("href")).replace("#", ""))
      link.setAttribute("href","/")
      // 配置 点击 触发新的锚点定位
      link.addEventListener("click", (event) => {
        event.preventDefault();
        // 这里的 addTop 是通过错位使得 toc 自动展开.
        volantis.scroll.to(target,{addTop: 5, observer:true})
        // Anchor id
        history.pushState(null, document.title, "#" + target.id);
      });
      return target;
    });

    function activateNavByIndex(target) {
      if (target.classList.contains("active-current")) return;

      document.querySelectorAll(".toc .active").forEach((element) => {
        element.classList.remove("active", "active-current");
      });
      target.classList.add("active", "active-current");
      let parent = target.parentNode;
      while (!parent.matches(".toc")) {
        if (parent.matches("li")) parent.classList.add("active");
        parent = parent.parentNode;
      }
    }

    // 方案一：
    volantis.activateNavIndex=0
    activateNavByIndex(navItems[volantis.activateNavIndex])
    volantis.scroll.push(()=>{
      if (targets[0].getBoundingClientRect().top >= 0) {
        volantis.activateNavIndex = 0
      }else if (targets[targets.length-1].getBoundingClientRect().top < 0) {
        volantis.activateNavIndex = targets.length-1
      } else {
        for (let index = 0; index < targets.length; index++) {
          const target0 = targets[index];
          const target1 = targets[(index+1)%targets.length];
          if (target0.getBoundingClientRect().top < 0&&target1.getBoundingClientRect().top >= 0) {
            volantis.activateNavIndex=index
            break;
          }
        }
      }
      activateNavByIndex(navItems[volantis.activateNavIndex])
    })

    // 方案二：
    // IntersectionObserver 不是完美精确到像素级别 也不是低延时性的
    // function findIndex(entries) {
    //   let index = 0;
    //   let entry = entries[index];
    //   if (entry.boundingClientRect.top > 0) {
    //     index = sections.indexOf(entry.target);
    //     return index === 0 ? 0 : index - 1;
    //   }
    //   for (; index < entries.length; index++) {
    //     if (entries[index].boundingClientRect.top <= 0) {
    //       entry = entries[index];
    //     } else {
    //       return sections.indexOf(entry.target);
    //     }
    //   }
    //   return sections.indexOf(entry.target);
    // }
    // function createIntersectionObserver(marginTop) {
    //   marginTop = Math.floor(marginTop + 10000);
    //   let intersectionObserver = new IntersectionObserver(
    //     (entries, observe) => {
    //       let scrollHeight = document.documentElement.scrollHeight;
    //       if (scrollHeight > marginTop) {
    //         observe.disconnect();
    //         createIntersectionObserver(scrollHeight);
    //         return;
    //       }
    //       let index = findIndex(entries);
    //       activateNavByIndex(navItems[index]);
    //     }, {
    //       rootMargin: marginTop + "px 0px -100% 0px",
    //       threshold: 0,
    //     }
    //   );
    //   sections.forEach((element) => {
    //     element && intersectionObserver.observe(element);
    //   });
    // }
    // createIntersectionObserver(document.documentElement.scrollHeight);
  }

  document.addEventListener("DOMContentLoaded", ()=>{
    volantis.requestAnimationFrame(listennSidebarTOC)
  });
  document.addEventListener("pjax:success", ()=>{
    volantis.requestAnimationFrame(listennSidebarTOC)
  });
</script>



<script>
  document.onreadystatechange = function () {
    if (document.readyState == 'complete') {
      // 页面加载完毕 样式加载失败，或是当前网速慢，或是开启了省流模式
      const { saveData, effectiveType } = navigator.connection || navigator.mozConnection || navigator.webkitConnection || {}
      if (getComputedStyle(document.querySelector("#safearea"), null)["display"] == "none" || saveData || /2g/.test(effectiveType)) {
        document.querySelectorAll(".reveal").forEach(function (e) {
          e.style["opacity"] = "1";
        });
        document.querySelector("#safearea").style["display"] = "block";
      }
    }
  }
</script>


  <script type="application/ld+json">[{"@context":"http://schema.org","@type":"Organization","name":"欢迎来到我的个人博客~","url":"http://8.219.11.28/","logo":{"@type":"ImageObject","url":"https://unpkg.com/volantis-static@0.0.1654736714924/media/org.volantis/blog/favicon/android-chrome-192x192.png","width":192,"height":192}},{"@context":"http://schema.org","@type":"Person","name":"天明","image":{"@type":"ImageObject","url":"https://unpkg.com/volantis-static@0.0.1654736714924/media/org.volantis/blog/favicon/android-chrome-192x192.png"},"url":"http://8.219.11.28/","sameAs":["https://github.com/volantis-x"],"description":"这些技术大部分是自己总结的，非官方"},{"@context":"http://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"http://8.219.11.28/","name":"欢迎来到我的个人博客~"}},{"@type":"ListItem","position":3,"item":{"@id":"http://8.219.11.28/2024/02/23/工具-Redis/"}}]},{"@context":"http://schema.org","@type":"WebSite","name":"欢迎来到我的个人博客~","url":"http://8.219.11.28/","keywords":null,"description":"这些技术大部分是自己总结的，非官方","author":{"@type":"Person","name":"天明","image":{"@type":"ImageObject","url":"https://unpkg.com/volantis-static@0.0.1654736714924/media/org.volantis/blog/favicon/android-chrome-192x192.png"},"url":"http://8.219.11.28/","description":"这些技术大部分是自己总结的，非官方"},"publisher":{"@type":"Organization","name":"欢迎来到我的个人博客~","url":"http://8.219.11.28/","logo":{"@type":"ImageObject","url":"https://unpkg.com/volantis-static@0.0.1654736714924/media/org.volantis/blog/favicon/android-chrome-192x192.png","width":192,"height":192}},"potentialAction":{"@type":"SearchAction","name":"Site Search","target":{"@type":"EntryPoint","urlTemplate":"http://8.219.11.28?s={search_term_string}"},"query-input":"required name=search_term_string"}},{"@context":"http://schema.org","@type":"BlogPosting","description":"这些技术大部分是自己总结的，非官方","inLanguage":"en","mainEntityOfPage":{"@type":"WebPage","@id":"http://8.219.11.28/2024/02/23/%E5%B7%A5%E5%85%B7-Redis/"},"author":{"@type":"Person","name":"天明","image":{"@type":"ImageObject","url":"https://unpkg.com/volantis-static@0.0.1654736714924/media/org.volantis/blog/favicon/android-chrome-192x192.png"},"url":"http://8.219.11.28/"},"publisher":{"@type":"Organization","name":"欢迎来到我的个人博客~","logo":{"@type":"ImageObject","url":"https://unpkg.com/volantis-static@0.0.1654736714924/media/org.volantis/blog/favicon/android-chrome-192x192.png","width":192,"height":192}},"url":"http://8.219.11.28/2024/02/23/%E5%B7%A5%E5%85%B7-Redis/","wordCount":0,"datePublished":"2024-02-23T14:27:45.468Z","dateModified":"2024-01-25T07:35:02.135Z","image":{"@type":"ImageObject","url":"https://unpkg.com/volantis-static@0.0.1654736714924/media/org.volantis/blog/favicon/android-chrome-192x192.png","width":192,"height":192}}]</script>



      
        <!--
  pjax重载区域接口：
  1.  <pjax></pjax> 标签 pjax 标签必须存在于所有页面 否则 pjax error
  2.  script[data-pjax]
  3.  .pjax-reload script
  4.  .pjax
-->



<script src="https://unpkg.com/volantis-static@0.0.1654736714924/libs/pjax/pjax.min.js"></script>


<script>
    var pjax;
    document.addEventListener('DOMContentLoaded', function () {
      pjax = new Pjax({
        elements: 'a[href]:not([href^="#"]):not([href="javascript:void(0)"]):not([pjax-fancybox]):not([onclick="return false;"]):not([onclick="return!1"]):not([target="_blank"]):not([target="view_window"]):not([href$=".xml"])',
        selectors: [
          "head title",
          "head meta[name=keywords]",
          "head meta[name=description]",
          
          "#l_main",
          "#pjax-header-nav-list",
          ".pjax",
          "pjax", // <pjax></pjax> 标签
          "script[data-pjax], .pjax-reload script" // script标签添加data-pjax 或 script标签外层添加.pjax-reload 的script代码段重载
        ],
        cacheBust: false,   // url 地址追加时间戳，用以避免浏览器缓存
        timeout: 5000,
        
      });
    });

    document.addEventListener('pjax:send', function (e) {
      //window.stop(); // 相当于点击了浏览器的停止按钮

      try {
        var currentUrl = window.location.pathname;
        var targetUrl = e.triggerElement.href;
        var banUrl = [""];
        if (banUrl[0] != "") {
          banUrl.forEach(item => {
            if(currentUrl.indexOf(item) != -1 || targetUrl.indexOf(item) != -1) {
              window.location.href = targetUrl;
            }
          });
        }
      } catch (error) {}

      // 使用 volantis.pjax.send 方法传入pjax:send回调函数 参见layout/_partial/scripts/global.ejs
      volantis.pjax.method.send.start();
    });

    document.addEventListener('pjax:complete', function () {
      // 使用 volantis.pjax.push 方法传入重载函数 参见layout/_partial/scripts/global.ejs
      volantis.pjax.method.complete.start();
    });

    document.addEventListener('pjax:error', function (e) {
      if(volantis.debug) {
        console.error(e);
        console.log('pjax error: \n' + JSON.stringify(e));
      }else{
        // 使用 volantis.pjax.error 方法传入pjax:error回调函数 参见layout/_partial/scripts/global.ejs
        volantis.pjax.method.error.start();
        window.location.href = e.triggerElement.href;
      }
    });
</script>

      
    </div>
    <!-- import body_end begin-->
    <!-- import body_end end-->
    <!-- Custom Files bodyEnd begin-->
    
    <!-- Custom Files bodyEnd end-->
    <!-- front-matter body_end begin -->
    <!-- front-matter body_end end -->
  </body>
</html>
